Binding an Ordered Relationship with an NSArrayController - cocoa

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.

Related

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.

Getting a unique identifier for each element of NSArrayController's content

I'm making a custom view that I want to be bindings/core data compatible and represent a collection of data (a la NSTableView)
Is there any way my view can refer to a specific subset of the elements in the collection (other than the current selection) after a change by the user?
A bit of context:
The view is going to display a number of user-moveable boxes in a 2D space. Each box corresponds to a record in the model. Several can be moved at once, and I can't rely on the delta value being the same for each box (so no adding a delta to each selected object).
I guess I'm looking for something like an id assigned to each element of the content array by NSArrayController, so that the view can associate that id with each box. My first thought was to use the the index in a content array, but this could be messed up by undo/redo. I could subclass NSArrayController and get it to auto-generate an id for each model item, but does cocoa already do something like this already? Feels like I might be missing something.
I should have mentioned that I originally tried keeping each of the content array's elements stored in the view (as Peter suggests), but had them stored as keys in an dictionary.
The objects in the view didn't match the keys in the dictionary, so I assumed this meant that NSArrayController changed the proxy objects it uses to stand for model objects.
However, it turned out that NSDictionary copies its keys, so it seems to be no good for situations where you want to associate a particular instance of an object with another.
NSMapTable is its more flexible cousin, and can be configured not to copy its keys.
Why not just refer to the objects themselves? You can keep them in a set or array, whichever is appropriate.
If you really need an identifier of some sort: What for? What are you going to do with it?

Bindings - master detail array controllers

I really hope someone can help on this because I'm learning cocoa and have hit a road block.
I am trying to model a simple poker tournament. For now, my entities are simply a Tournament (with a number) and a Player (with a Name). A Tournament has an array of Players.
I can bind two independent table views to display the tournaments and the players just fine. But I want the players table view to just show the players that belong to the selected tournament from the first table view.
Each has it's own array controller. I have tried a variety of different bindings for the second (players) table but to no avail. Has anyone accomplished this? If so maybe you could spell it out for me, as I there are few examples online.
Update
I can now ALMOST get where I need to, mostly through rial and error and hours of googling. I have bound the player AC's content to the tournament AC, with controller key 'selected objects' and Model Key Path 'players', which is the name of the array in my Tournament entity.
I have the bound the column in the players table view to this second Player AC, controller key arranged objects. But what to put in the Model Key Path? I know it is working because if I stick #count in there I get the correct number of players for the selected tournament. But 'name' and 'player.name' are no good. Is there any kind of 'item.name' or 'players.item.name' I can try?
Sooo close, thanks for the help so far:
I think this tutorial will help you. They also create a master/detail view.
In short: Bind the contentArray of your player's array controller to the tournament's array controller, set ControllerKey to selection and the remaining properties accordingly to your model.
I found the answer here:
Implementing parent->child drill down in Cocoa with Core Data bindings that span multiple entities.
The child controller needs to know about the managedObjectContext through its own binding.
The child controller must not be in Entity Mode, but rather operate as a NSMutableDictionary class.
And, finally, the child controller does not prepare its data. It retrieves it from the parent, through the Content Set binding. Use the controller key selection, and the model key path that connects to the children.
I'm surprised this is not a more commonly used practice, and hope the next person reading this doesn't spend so long finding the answer!

What's the best way to model _ordered_ lists of items with core data (Mac OS X 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.

Resources