Two NSArrayControllers on one NSManagedObjectContext? - macos

I have two NSArrayControllers in my MainWindow.xib and want to use both of them with CoreData. Both are using entities of the same type from the same data model. These two array controllers are bound each to their own table views (one for generated data, one for stored data).
The first array controller is connected to the managed object context in AppDelegate but what do I do about the second array controller? It doesn't seem it can be connected to the same managed object context. If I compile now I get the error Cannot perform operation without a managed object context for the second array controller.

To answer this question on my own now:
The binding inspector in IB has bindings on NSArrayController for the Managed Object Context. I wasn't completely sure that this is the right way to bind the array controllers but it obviously seems to be so.

Related

Having an NSSet as part of an NSManagedObject in Core Data

I have a class which as its properties has a number of NSSet values. The content of these sets is not stored in Core Data, but comes from another source. They are basically ID strings which are unique. No point in filling up Core Data with them as separate entities.
However, as far as I can see it's not possible to store an NSSet as an attribute of an NSManagedObject. I guess I would need to serialise it into binary data by hand, whenever the object gets stored, and deserialise it when it gets retrieved from the persistent store? The same would also apply to storing other collection classes.
Has anybody else ever come across this issue and is able to give some advice?
You can set the attribute type to 'transformable' and then directly store the set into the attribute. Be sure that all of the contents of the set conform to < NSCoding >.
In Xcode, in the Project Navigator open up the project_name.xcdatamodeld source for the model editor and select the entity in question. Choose Editor in the Xcode menu bar and there is a Create NSManagedObject subclass... item. Make the subclass and use it for extra non-Core Data properties. The subclass becomes part of the model in place of the entity. It retains all of the entity's behaviors.

Core Data: "-add<RelationshipKey>Object:" not called

Let's presume I have an entity BikeRider with a relationship property called helmets.
I have an array controller bound to the app's managed object context, with entity set to BikeRider. There's a tableview that lists all bike riders.
Then, I have a second array controller, bound to the app's managed object context, with entity set to Helmet. Additionally, it's bound to bikeRiderArrayController.selection. There's a second tableview that lists all helmets for the selected bike rider.
I also have two buttons for adding and removing helmets. The setup works apparently flawlessly.
Except, of course for one small thing: it looks like -addHelmetsObject:, -removeHelmetsObject:, -addHelmets: and -removeHelmets: never get called. This means some code for setting up observation of each helmet's color property never gets called.
What am I missing? Isn't overriding addHelmets: et al (with proper willChangeValueForKey: et al notifications) the right way to get notified of additions?
Do I really have to [self observeValueForKey:#"helmets". . .] and then [oldValue minusSet:newValue] and vice versa to figure out which objects were added or removed? I could swear the methods were being correctly called in the past. Maybe some key element of the setup is now different.
This has never worked properly through NSArrayController. From Apple's docs:
Custom relationship set mutator methods are not invoked by an arraycontroller
Problem: You have implemented set mutator methods for a relationship as described in “Custom To-Many Relationship Accessor
Methods,” and have bound the contentSet binding of an
NSArrayController instance to a relationship, but the set mutator
methods are not invoked when you add objects to and remove objects
from the array controller.
Cause: This is a bug.
Remedy: You can work around this by adding self to the contentSet binding's key path. For example, instead of binding to [Department
Object Controller].selection.employees, you would bind to [Department
Object Controller].selection.self.employees.

CoreData, SourceList and NSTreeController

Ok guys, here is my question.
I would use CoreData + Source List + NSTreeController to create something similar to the source list of Finder (or iTunes if you prefer).
My problem is that what I have in my source list doesn't come entirely from CoreData. For example I have a section (root) called "data from core data" that is populated using CoreData, but I also have some "fake" sections (i.e. "Favorites" section) that are created run-time.
For this reason I think that binding the OutlineView and the NSTreeController directly to CoreData using entities is not feasible and that I have to use something intermediate like a NSMutableArray that I populate at start with data coming from CoreData and the "fake" sections using ad-hoc classes for the members (like in the "SourceView" example by Apple). This is the first question: (1) Am I right in doing this?
My second question is about this NSMutableArray. Let's say that using this array could be a solution, I was wondering which is the correct way to fill the array in. In the example "SourceView" the intermediate array is populated run-time using the NSTreeController (insertObject:), but another solution I was thinking of is subclassing NSMutableArray and change directly the content of the array. (2) Am I breaking the MVC in this way?
One approach to mix persistent nodes with temporary nodes which only exist at runtime, is to add a NSInMemoryStoreType store to your persistentStoreCoordinator. Here is a tutorial on how to do this: http://simplyhacking.com/source-list-with-core-data.html

Deleteing a managed object from an entity

I've got a managedObjectContext with 2 entities, each of which contains multiple entries. Objects of Entity A are represented in a Table View, and I want the user to be able to delete any entity is Entity A. My problem is that, when I send the request to delete that entry, the wrong entity is being called!
FYI, I’m handling the deleting process in a separate method, so when the delete button in Table view is triggered, before it’s been taken out of the view, I first want to make sure it’s been removed from the managedObjectContext.
- (BOOL) deleteCompletedSuccessfully : (EntityA *) anEntry
{
[self.managedObjectContext deleteObject: anEntry];
NSError *error = nil;
If (![self.managedObjectContext save:&error])
{
NSLog (#”%#”, [error userInfo]);
return NO;
}
return YES;
}
The error is:
Error Domain=NSCocoaErrorDomain Code=1570 \"The operation couldn\U2019t be completed. (Cocoa error 1570.)…. And the rest of the error message indicates that I’m trying to delete a nil object in EntityB!!!! While anEntry is in fact in EntityA.
I tried encapsulating the input (anEntry is this case) into an array, i.e:
- (BOOL) deleteCompletedSuccessfully : (NSArray *) array
{
EntryA *anEntry = [array objectAtIndex: 0];
// and the rest of the code
The same error. How can I make it look for that particular entry in a particular entity?!
Any help?
I think you've got some conceptual confusion going on here between entities and managed objects.
Entities are abstractions analogous to Classes. Managed objects are actual individual instances of NSManagedObject or one of its subclasses. Entities in the data model tell the managed object context what attributes and relationships to one another the managed object instances will have.
Entities exist solely in the data model whereas Managed objects are in the "object graph" which is the actual functioning group of related objects alive in memory. Entities simply describe to the managed object context how everything fits together much as a class definition tells the compiler how all the properties and behaviors of fit together in a class. Managed objects as instances have data and behaviors just like all other live objects.
Likewise, a managed object context does not add, remove or set the values of entities in any way. Instead, it adds, removes or set the values of managed objects configured by the entities in it's data model.
So, when you say:
I've got a managedObjectContext with 2
entities, each of which contains
multiple entities.
What you really mean is:
I've got a data model with 2 entities and a managed object context with many managed objects configured by those entities.
A tableview may display only the data from the instances configured to one entity (that is most common) but the actual data and the insertions and deletion happen to managed object instances and not the entities which are unalterable by that point.
However, I don't think the terminology confusion is the actual cause of your problem. Instead, I think the error is trying to tell you that you are deleting an object configured by EntityA from a required relationship with a an object configured with entityB.
The cocoa error 1570 is a NSValidationMissingMandatoryPropertyError which as the name suggest occurs when you try to save a managed object that has a required property with a nil value. The manage object context tries to validate the object graph before it saves and when it finds a missing required property it throws that error.
I can't tell you anything more because I have no idea what your data model looks like.

Can a NSManagedObject own another as a property?

I've taught myself Obj-C, and have been self-teaching Cocoa, but adding Core Data to my program has given me nothing but a huge headache, thanks to me needing extensive custom logic. Here are a couple of the questions that are driving me insane.
What if I want a Managed Object to own another Managed Object? It seems if I give it a to-many relationship, the owned object will simply be shared by various masters, but I want each Owner to have its own.
If I subclass an NSManagedObject, can I make simple calls to the Array Controller to remove or copy instances of ManagedObject, and assume those will be translated into the Core Data model?
If I want to programmatically edit the properties of a ManagedObject, can I get away with mere KVC calls to the Array Controller? What's all this talk about NSPredicate an NSFetchRequest to the NSManagedObjectContext from the NSManagedDataStoreDrivingMeCrazy? Can I make an NSFetchRequest that filters the relationships of an object currently selected in a table view?
Once I use a fetch request to get a group of objects, how do I go about querying their relations? Does that require a whole other FetchRequest, Predicate, and so forth? Isn't Core Data supposed to be easier? Am I missing something?
An entity is similar to a class--it's a blueprint for a managed object that will be instantiated later. Each managed object will have its own attributes and relationships to configure.
You can definitely insert and delete managed objects. You might have to do some code to support copying, but I am not sure.
Yes, the properties (attributes and relationships) of managed objects support KVC (and KVO and bindings).
You can access the object or set of objects simply by using the relationship name that you define in the model (no additional fetch or logic is required).

Resources