rentzsch.com: tales from the red shed

mogenerator: Core Data codegen

Code

Core Data, like its Enterprise Objects Framework (EOF) ancestor, offers to represent your persistent object instances as classes of a generic data class (NSManagedObject in Core Data, EOGenericRecord in EOF) or a custom subclass of the generic class.

There’s a couple of reasons why you want a custom subclass:

  • Business logic. The biggie. Where do you put your entity’s behavior? Subclassing and adding methods is the obvious choice. (Some folks try to get away with just adding categories on NSManagedObject. This is bad architecture in my book.)
  • Type safety. I’m tempted to move Love 6: Static Typing higher on my Love List, because ObjC just Gets Typing Right.

    I know it’s fashionable to disparage type-safety nowadays, but you’ll notice the sound and fury originates mostly from folks whose languages favor the Bondage and Discipline side of the type-safety coin. They’re right to complain, but they’re blind to that it can be better.

    Since ObjC gets type-safety right, it’s a shame to throw away that advantage by just using -objectForKey: everywhere. By subclassing NSManagedObject and offering real getter+setters with real type information, a lot of compile-time error-checking machinery springs to life. Don’t you want the machine on your side?

While in the common case you want custom subclasses for the reason mentions above, the real-world common case is to avoid custom subclasses everywhere you can. Why the disparity?

Because custom subclasses are hella painful.

Sure, Xcode goes through the trouble of writing the code for you (Design -> Data Model -> Copy Method [Implementations|Declarations] to Clipboard) but it sucks in a number of ways:

  • Two menus commands. For each entity. If you have an app with five entities, you’re now mindless GUI-driving land. Open data model view. Click the entity. Select all its properties. (Make sure you’re on the “Show All Properties” view first, that one’s bitten me before — where did all my relationship code go?!?) Design -> Data Model -> Copy Method Declarations to Clipboard. Command-0. Locate and open entity header file. Get your text selection right, avoiding your custom methods. Paste. Back to data model window. Design -> Data Model -> Copy Method Implementations to Clipboard. Command-`. Counterpart. Text selection again. Scroll to get it right. Paste again. Repeat all that nine more times.

  • Conflates machine-generated code and human-written custom code. The common (and misguided, but “easy”) method is to place both generated and custom code into the same file. So, for example, you have an entity Employee. The means you’ll have one header file Employee.h and one source file Employee.m. Both files will contain generated and custom code, which makes updating the generated code harder than it should be.

    The better technique is to put machine-generated and custom code into their own files. Once we know a certain file will not be touched by a human, we can blow it away and update it with impunity, easily keeping it in sync with the model.

    This technique plays well with OOP: you can make the custom code subclass the generated code, easily adding methods and replacing stock behavior in a language-supported fashion. To extend our previous example, we’d have four files for each entity: the machine-generated class _Employee.h and _Employee.m (which house the Employee class) and the human-maintained Employee.h and Employee.m files (whose Employee is a subclass of _Employee ).

    Now, there’s nothing in Xcode to prevent you from using this technique — I’ve just never have seen it in the wild outside my projects since maintaining four files per entity is nonobvious and can get overwhelming if you don’t automate the process.

    This technique, by the way, has its own design pattern named Generation Gap, and is old-hat to EOF graybeards thanks to the awesome eogenerator.

What we need is eogenerator, for Core Data. Enter mogenerator: Managed Object Generator.

It’s a command-line tool that automates the generation of custom subclasses. Point it at your .xcdatamodel file and it will spew out four files per entity, two for you, two for the machine.

The magic happens when you modify your data model. One invocation will update all the code to keep it in sync with your model, while leaving your custom code untouched.

Right now the code generated by mogenerator is already ahead of what you get from Xcode: it will automatically generate methods that will box/unbox scalar values for you (so you get both a -(NSNumber*)employeeAge method and a -(short)employeeAgeValue method). In the future, I plan on adding type-safe fetch request wrappers as well — I had that working with EOF+eogenerator (with model-based fetchspecs) and I miss it already.

Right now things are working, but raw. I need to get my Xcode integration story tight, write some docs, an install script and then do a real binary release (instead of just pointing to a SourceForge subversion folder). But that won’t happen for a little while, so here’s my “release early, release often” shot. More postings as it matures.

Sunday, October 01, 2006
12:00 AM