In Part 1 we addressed the current state of Core Data programming in MacRuby, and discussed some of the problems and challenges facing Rails programmers accustomed to ActiveRecord. Now let’s address some solutions.
First, some necessary terminology.
These are Core Data concepts that you must be familiar with before you’ll be effective at Core Data persistence in MacRuby (or Objective-C, for that matter).
The Managed Object Model (or MOM) keeps track of objects and their relationships. It doesn’t care about actual persistence; you might think of it as just a picture of the data structures, their attributes and their relationships.
In a Core Data-enabled project there is an .xcdatamodeld file. It contains the model definitions. When you compile your app, this is compiled into a mom file and stored in a folder with a .momd extension. This is how the application knows about the data structures and relationships to use.
The Managed Object Context (or MOC) acts as a bridge between the MOM and the actual persistence mechanism. You can think of the MOC as an in-memory scratch pad. When you fetch objects from a persistent store, you bring temporary copies into the scratch pad where they form an object graph. You can then modify those objects however you like. Unless you actually save those changes, however, the persistent store remains unaltered.
The Persistent Store Coordinator (or PSC) This is the actual persistence mechanism, and is connected to a MOC. The PSC is analogous to the actual data storage layer, whether it’s SQLite, XML, or whatever. When you fetch objects, the MOC asks the PSC to return those objects that match the fetch request.
An Entity is most basic building block of a Managed Object Model. It’s a description of something you want to store, such as an author or a mailbox.
A Predicate is a general means of specifying queries in Cocoa, i.e. a conditional. A predicate is your WHERE statement. Predicates have their own syntax in Core Data and it isn’t exactly SQL.
A Sort Descriptor is a means of specifying sorting, i.e. your ORDER BY statement.
Putting it all together (or, “just show me how to retrieve data, please!”)
Fair enough, I know you’re impatient. Here’s my simplification of the whole complicated mess.
I employ a singleton class called Datastore to manage data retrieval. I made it a singleton so that I could access a single MOC, MOM, and PSC from anywhere in my application. You can see my current version of Datastore.rb in this Gist and I encourage you to download it and use it in your own applications. There’s no particular magic there, it’s just a tidy way to keep Core Data code organized.
The main thing that my Datastore.rb gives you is single-line object retrieval. Customarily data retrieval in Cocoa is a highly verbose, un-DRY process. Don’t do that. Simply add Datastore.rb to your MacRuby project, and use my find_by_entity_name() method to do the heavy lifting for you.
Usage examples (don’t forget to reference the Predicate Syntax!)
An easy one: “find all Waypoints whose identifier begins with Z”
wpts = find_by_entity_name('Waypoint', withPredicate:"identifier like 'Z*'")
“retrieve waypoints 16801, 16802, 16803”
wpts = find_by_entity_name('Waypoint', withPredicate:"(SELF in %@)", [16801, 16802, 16803])
“retrieve waypoints SEA, PDX and SAN, ordering by identifier”
wpts = find_by_entity_name('Waypoint', withLimit:25, withOrder:"identifier", withPredicate:"(SELF.identifier in %@)", ['SEA','PDX','SAN'])
Now a slightly more verbose query in which we spread out across several lines:
sort_desc = NSSortDescriptor.alloc.initWithKey("identifier", ascending:true) pred = NSPredicate.predicateWithFormat("identifier BEGINSWITH 'Z'", nil) wpts = Datastore.find_by_entity_name('Waypoint', withLimit:25, withOrder:sort_desc, withPredicate:pred)