What's the best way to model _ordered_ lists of items with core data (Mac OS X cocoa)? - cocoa

What's the best way to model ordered lists of items with core data?

As of OS X 10.6, there is no automatic way to maintain an ordered to-many relation in Core Data. You will have to add an "index" attribute to the target of the to-many relation and maintain this index manually or maintain a mapping from object ID to index in the entity that has the ordered to-many relationship. It's actually not that hard to implement the first of these options by overriding the KVC orderd to-many accessor and setter methods. You can also create a fetched property on the parent entity to make (read-only) access to the ordered list easier by adding a sort descriptor to the fetched property's fetch predicate.

If you are targeting Lion or newer you can create ordered to-many relationships directly in the data modeler. Those use the NSOrderedSet class.

Related

Filter to-many related/child entities of NSManagedObject

I have an entity called Playlist which has a to-many relation with Event entity called events.
I also have NSArrayController configured in Entity Mode for managing Playlist entities
I need to Pre Fetch and filter events based on a criteria. The question is where exactly should I fetch events?
a. In Playlist's awakeFromFetch
b. In Playlist contoller's fetchWithRequest
c. Or is there a way to pre-fetch related entities based on a filter criteria?

Binding an Ordered Relationship with an NSArrayController

How does one bind an NSArrayController's content to the entities in an ordered to-many relationship?
I have an unordered to-many relationship in my Core Data model, and an NSArrayController whose Content Set is bound to that relationship from the parent entity. This works fine, the data is accessible from the NSArrayController, no problem.
I decided during development that it would be better to allow users to manually reorder these child objects, so I changed the relationship to an ordered one. Now, when my NSArrayController is being created from my nib, the following error is presented:
Cannot create NSSet from object Relationship '...' fault on managed object ... of class _NSFaultingMutableOrderedSet
Now actually, I think this all makes sense: It's an ordered relationship, so now I'm getting an ordered set. Binding it to Content Array also would be inappropriate, since it's now an NSOrderedSet, not an array. My question is: Now how do I bind this relationship's data back into the NSArrayController?
I came across this discussion while searching to see if there've been any new developments on this front. In a shipping app I currently bind the array controller's content array to orderedSetKey.#array and it works just fine, not sure if I discovered that myself or if someone else suggested it somewhere.
The fundamental problem is that a Core Data ordered to-many relationship returns an NSOrderedSet, and NSOrderedSet is not a subclass of NSSet. Any array controller bindings that expect an NSSet will fail.
Tom Fewster has a detailed blog post describing the use of NSValueTransformer to work around this shortcoming, converting between NSOrderedSet and NSArray on the fly. He also provides a sample implementation on Github.

MVC Design: How many array controllers do I need?

I have a pretty straightforward MVC design question.
I've got a class with a bunch of properties, and a form to present an instance of the class. Several of the class properties are arrays - some are arrays of NSStrings to be presented in a one-dimensional table view, and some are arrays of sub-objects to be presented in a two-dimensional table view (one column per sub-object property). I don't actually want to do anything with the data in any of these tables - just present the contents in a scrollable, read-only table view.
During my first attempt at bindings, I added an object controller bound to the class instance. Then, I tried to bind each column of each table view to the "selection" member of the class, with a model key path specifying the array property of the instance (and, for the two-dimensional tables, a member of the sub-object). I was surprised that this didn't work for the columns of the one-dimensional tables.
Next, I added one array controller for every table, binding it to the "selection" member of the object controller. For the one-dimensional tables, I bound the column to the array controller with no model key path; for the two-dimensional tables, I bound the column to the array controller with a model key path specifying a property of the sub-object. This works - but for a window with seven tables, I have seven array controllers! That feels like overkill, since the tables aren't doing anything other than presenting data.
My question is simple: Is my design in line with good MVC practice - do I really need all of these array controllers? Or is there a simpler way to specify my bindings (for one-dimensional and/or two-dimensional tables) that will enable me to eliminate some array controllers? When I have an array of strings in an object to be displayed in a one-column table, it feels like overkill to use an array controller bound to the object and the table.
As an ancillary question - do I really need to worry about excessive array controllers? Are they lightweight objects that I should use liberally, or resource-intensive objects that I should conserve, especially in limited resource contexts like iOS?

Adding to to-many / many-to-many core data relationship

I have 2 Entities, related by a many-to-many relationship.
Thing<<->>Tag
There is one NSArrayController controlling the entity "Tag", bound to the managedObjectContext. By the array controllers add: and remove: action i can add instances of tag to the collection.
There is a second NSArrayController controlling "Thing" entities, also bound to the managedObjectContext.
So each of the controllers manages all instances of their entity.
Now, let's say there are 5 "tag" and 3 "thing" instances already created by their array controllers.
I'd like to link individual tags to a thing. I just want to create the relationship between an existing thing to an existing tag instance.
Is addObject: of NSArrayController the right method for that? Or does it create a new managedObject?
Would it be equivalent to:
NSMutableSet *tags = [aThing mutableSetValueForKey:#"tags"];
[tags addObject:existingTag];
?
Is there some best practice for a tagging system?
I've found it helpful (in the latest version of Xcode) to select the entity in the core data modeller, and then go to the file menu, and select new file -> Core Data -> NSManagedObject subclass. It automatically creates a class with the necessary members AND ALSO methods for adding objects in the toMany relationships.
If you've done that, then you just need to get ahold of the thing instance to which you want to add a tag and you can call the method declared for you to do so. How that method is named is obvious from the header file generated.

Making entity framework treat views with many-to-many relationships, like it does tables with many-to-many relationships

I have three views that I've manually created in the DB.
First view is "Region", the second is "FIPS" and the last is a many-to-many between them called "Region2FIPS". These are all views, and I only need read access the data, so I'm not worried about having updateable views.
I have added each of these views to Entity Framework, and created the appropriate associations between them.
Region to Region2FIPS is a 1 to many.
FIPS to Region2FIPS is a 1 to many.
The "Region2FIPS" view contains only two columns, one called "FIPSID" the other "RegionID". These column are associated with their respective views in the relationships I defined above.
When this type of association is made on tables in the DB, Entity Framework knows that it is a many-to-many relationship and it creates a navigation property on "Region" called "FIPS" that I can use to navigate through the child collection of FIPS. It does likewise for "FIPS" to "Region".
However, when done manually, with views, it does not exhibit that behavior. Instead, my "Region" object has a collection of "Region2FIPS" objects, which each have a navigation property called "FIPS" which is of type "FIPS". And my "FIPS" object has a collection of "Region2FIPS" objects, which each have a navigation property called "Regions" of type "Region".
I assume this has something to do with the fact that I can't create foreign key references on the views, so entity framework doesn't realize the many-to-many relationship. But I thought that if I manually created the many-to-many relationship between the views it would recognize it and properly handle the navigation between the types. Is there a way for me to force it to do this?
It's possible, but the designer doesn't really help you here. You have to do the mapping manually.
One fairly easy way is to use Code First mapping. But this means your model has to be Code First to begin with. If you're writing a new model, just do that.
If you're using DB First mapping, however, you will have to do the mapping manually. Your SSDL will probably already be correct, once you define the "primary keys" of the views. You would then have to remove the "Region2FIPS" objects from the CSDL (not just from the designer!) and manually patch up the MSL.
Perhaps the easiest way to do this would be to use the designer to automatically map real DB tables (not views) with a similar schema and then replace the table names with view names in the EDMX, using the XML editor.

Resources