The Generation Gap design pattern and CoreData

Paul Stringer
Stringer’s Theory
7 min readJun 17, 2015

CoreData is Apple’s data storage solution for OS X and iOS, an essential piece of the Cocoa stack of technologies. Its use is considered so common place that it even gets its very own tick box when creating any new Xcode project. It probably needs little introduction, but in simple terms it holds to a lofty goal of automating and simplifying the complexity of working with data. Something every App must do and something that’s very hard to do well on your own.

We’ll follow its history and discuss one of the biggest things you can do to improve your productivity when working with CoreData through the use of a classic design pattern and a fascinating little tool called MOGenerator.

The ORM is Born

CoreData’s back story is long and worthy of a few moments to revisit. It begins in 1992 and arrives in the form of DatabaseKit, an early product from parents NeXT.

As NeXT engineers were deeply immersed in Object Oriented design (the avant-garde of software engineering at this time), DBKit was born of an attempt at marrying those OOP principles with the relational databases which were starting to take over the world. And so it was, the very first ORM was born.

DBKit was hurriedly followed by a more robust version, the Enterprise Objects Framework (EOF). For a long time this became, if not a cash cow, a life line for NeXT along with its niche but advanced WebObjects web development framework. To this day, this still runs many of Apple’s own internal and external systems such as the Apple Store, iTunes, Developer Portal and more.

Fast forward to present day and EOF was rebooted as CoreData many years later with the ongoing remix of NeXT technologies that was taking place at Apple during the 2000s. This finally became mainstream with its introduction to new audience of developers in iOS 3.0.

Stop SQueLing!

The simple idea behind CoreData remains the same now as it was with DBKit and other ORMs. Abstract the underlying storage and retrival system to nothing more than an implementation detail. In the world of relational databases this is simply, data structures stored as tables and rows are migrated to data structures represented by objects and properties. This is entirely for the convienience of Object Orientated code who don’t want to speak SQL.

As the mechanics of storage have been abstracted to a lower level, this decouples our code from the language of the storage through this additional layer of indirection. With this, in theory you could potentially change from a database implementation to a RESTful API based one with very little change.

The real fundamental understanding to grasp is that you’re not working with a database, you’re working with a generalised solution to object graph management. In CoreData’s case you can easily make the change of how things are persisted to suit your requirements without re-writing all the code.

As an exercise imagine the code changes you would need to make to transition from an SQL Store to an In-Memory one. You make this change by changing just one line of code. The one where we specifiy the store type via a simple string.

- (NSPersistentStore *)addPersistentStoreWithType:(NSString *)storeType configuration:(NSString *)configuration URL:(NSURL *)storeURL options:(NSDictionary *)options error:(NSError **)error]

Writes all the Code

“Code generators increase your productivity and help avoid duplication.” –The Pragmatic Programmer

To facilitate the mechanics of CoreData, code generation has always been a essential step between you and your model. This feature is reasonably well hidden in Xcode so here’s a quick reminder of how it works

  1. Create a project using CoreData in Xcode.
  2. Add an entity or two with some attribites to your model.
  3. Add New Files.. choose CoreData and the ‘NSManagedObject subclass’
  4. Select your Model and Entities

After completing these steps you’ll see some rudimentary code generation magic at work as your modelled entities spring to life as classes you can see rather than an abstract notion of tables and rows you can’t.

As often is the case with some Apple tools it gives you just enough to get you started. On any project of anything other than the most basic of Apps though, you’re going to quickly come across a limitation in this approach. That of updates.

Humans v Machines

Take the following situation. You create your new entity, write some code and everything’s great. Then you decide to add something else to your entity model. Already you may find yourself at an impasse created by the default approach.

If you follow the code generation route with Xcode you’ll meet the dialog box “The following files already exist and will be replaced:”. This is fine if you didn’t add any code to your entity. But the chances are at some point along the way you’ve been seduced into doing so. Now although this situation is contrived and one which any developer can quickly learn strategies to deal with, it serves the purpose of illustrating the problem. It’s also the time when a good developer at this point may ask the question, what is the right way, has this been solved already? Turns out it has.

Taking a step back. What happened, what are we trying to achieve and what are we trying to avoid? Well it’s clear we wanted to add code to our entities class but in doing so we unwittingly have violated the basic principle of code generation. This principle is that it must always be ok for the code that’s been generated to be regenerated by the machine at any time. E.g. you must never touch the generated code. By modifying the generated class we violated this and must now consider the options available for not doing so again.

There are really only two approaches to the problem you can adopt.

  1. Add your code via Categories/Extensions on your Entity classes.
  2. Create subclasses of your entities and add your human code to your subclass.

Both approaches mean you never touch the generated code and it’s free to be regenerated at any time achieveing the goal. Option one is the simpler approach and takes advantage of a specific language feature of Objective-C and Swift.

The second approach is an implementation of the Generation Gap design pattern. Martin Fowler concisely describes this pattern as being this.

“Generation Gap is about keeping the generated and handwritten parts separate by putting them in different classes linked by inheritance.” — http://martinfowler.com/dslCatalog/generationGap.html

Effortlessly Preserved

Generation Gap implemented with MOGenerator is the approach we’ll focus on. In addition MOGenerator automates to such a degree the mechanics of working with CoreData that you’d be masochistic not to use it. As a human you have much better things to be doing.

MOGenerator Generates Objective-C code for your Core Data custom classes. Unlike Xcode, MOGenerator manages two classes per entity: one for machines, one for humans. The machine class can always be overwritten to match the data model, with humans’ work effortlessly preserved

In addition the overview of EOGenerator (the EOF forerunner to MOGenerator) elaborates further on this with a description of a powerful features of both these tools.

When using customized templates, it is possible (among other things) to generate methods to call fetch specifications, generate methods to add and remove objects from relationships with typed arguments, to have a custom superclass for all your MOs, and generate cross-framework #import statements correctly for Objective-C classes when models are in separate frameworks. Example templates are provided that show some of these additional abilities.

More templates, less code

Simple and powerful stuff. Integrating MOGenerator into your project gives flexibility and increased productivity provided via an elegant implementation of the Generation Gap pattern. What makes the tool powerful is its extensibility via use of templates that can be modified. Templates intially just generate the getters and setters, but they can also take on many more jobs. There’s a lot of boiler plate code with CoreData. Why not also have that written for us?

Check out this example of some basic CoreData code before and after using MOGenerator.

Before…

// Insert User
User *user = (User*)[NSEntityDescription insertNewObjectForEntityForName:@"User" inManagedObjectContext:moc]
// Fetch Request Fetching Users
NSManagedObjectModel *model = [[moc persistentStoreCoordinator] managedObjectModel];
NSDictionary *substitutionVariables = @{@“firstName" : name}
NSFetchRequest *fetchRequest = [model fetchRequestFromTemplateWithName:@“UserWithName” substitutionVariables:substitutionVariables];
// Check a Boolean Attribute
if ([user.isAdmin boolValue]) [self allowAccessAllAreas]

And after..

// Insert User
User *user = [User insertInManagedObjectContext:moc];
// Fetch Request Fetching Users
NSFetchRequest *fetchRequest = [User fetchRequestFetchingUsersWithName:moc name:name];
// Check a Boolean Attribute
if (user.isAdminValue) [self allowAccessAllAreas]

After using MOGenerator we have only as many lines of code as there are comments! The code is now so self documenting you could even lose the comments too. This example alone should be enough to convince you that if you’re working with CoreData then MOGenerator is an essential part of your tooling.

If you want to get started, it’s easy. Here’s how.

Begin at the GitHub page for MOGenerator. In addition, this excellent write-up “Setting Up MOGenerator in Xcode” walks you through integration of MOGenerator with your project that automates the generation step with every build.

Dance Free!

Using MOGenerator will improve your efficiency in working with the mechanics of updating your model as well as providing automated code for standard boiler-plate like fetching entities. I guarantee this will make your life with CoreData more enjoyable from day one.

“Oh dance free you imps, dance free!”

Finally, with all this new found freedom and spare time take a moment to read Dance you Imps! by Robert C. Martin. Read it to the end and consider why this all might actually be a really bad idea and one we should approach with a healthy dose of caution.

--

--