The focus of my talks for 2015 is the Core Data stack. There is a wealth of information on the internet about Core Data. Some of it is good, some of it is bad, and some of it is out of date. The goal of this article is to show how I am currently building a Core Data stack in Objective-C in 2015.

When the new NSManagedObjectContext designs were introduced in iOS 5, I was extremely excited by the changes. They helped to define one of the trickiest parts of Core Data and make it easier to do it right. That is the area of using Core Data in a multi-threaded environment.

A lot of advice on the internet goes along the lines of “Core Data is not thread-safe”. That is such terrible advice.

Core Data is thread-safe. It is more thread-safe than your UI.

However, you need to use it right.

If you have been to one of my talks this year, you have heard me mention my multi-MOC design pattern and how I recommend it for all but the edge cases.

What does it look like?

The Basic Design

First, my Core Data stack consists of a minimum of two NSManagedObjectContext instances.

1 Private Queue Context. The private queue context has one job in life. It writes to disk. Such a simple and yet vital job in the application. We build this as a private queue because we specifically want it to be asynchronous from the UI. We want to avoid locking the UI as much as possible because of the persistence layer.

2 Main Queue Context. This is the application’s Single Source Of Truth. This is the NSManagedObjectContext that the application will use for all user interaction. If we need to display something to the user, we use this context. If the user is going to edit something, we use this context. No exceptions.

I said earlier that there is a minimum of two contexts. There can be more, and potentially a lot more. The rest of the contexts will be children of the Main Queue Context. Their jobs vary, but they are always for non-user data manipulation.

What Does This Look Like?

I do not use Apple’s template for creating the Core Data stack. I have a significantly different design that I spoke about a couple of years ago.

I do not create the Core Data stack in the App Delegate. That is not where it belongs. I create it in its own controller class. Our persistence layer is far too important to be delegated to the Application Delegate. It deserves a proper controller of its own.


typedef void (^InitCallbackBlock)(void);
@interface MCPersistenceController : NSObject
  @property (strong, readonly) NSManagedObjectContext *managedObjectContext;
  - (id)initWithCallback:(InitCallbackBlock)callback;
  - (void)save;

The interface always starts out this simple. We have an initialization, access to the main queue managedObjectContext and a -save method. More methods develop over time as the application matures.

The callback is a bit new. The idea here is that the Application Delegate will call this controller right away. Then this controller will tell the Application Delegate when the persistence layer is ready to be used with the expectation that the Application Delegate will then complete the user interface and start displaying data. This helps us to avoid those crash on launch bugs that we all love so much.

Inside of the MCPersistenceController things are a bit more complicated:


#import "MCPersistenceController.h"
@interface MCPersistenceController()
  @property (strong, readwrite) NSManagedObjectContext *managedObjectContext;
  @property (strong) NSManagedObjectContext *privateContext;
  @property (copy) InitCallbackBlock initCallback;

  - (void)initializeCoreData;
@end

Firstly, we have a class continuation that updates the property for the managedObjectContext and gives us read/write access to it. It also adds a property for the privateContext and we make a copy of the callback block that gets passed in. Finally, we declare an internal method that will do the initialization of the Core Data stack.


@implementation MCPersistenceController

- (id)initWithCallback:(InitCallbackBlock)callback;
{
  if (!(self = [super init])) return nil;

  [self setInitCallback:callback];
  [self initializeCoreData];

  return self;
}

The initialization of the MCPersistenceController is straight forward. We call super init and when that is successful we make a copy of the block that is passed in and then start the initialization of the Core Data stack. This method is intended to return very quickly.


- (void)initializeCoreData
{
  if ([self managedObjectContext]) return;

  NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"DataModel" withExtension:@"momd"];
  NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
  ZAssert(mom, @"%@:%@ No model to generate a store from", [self class], NSStringFromSelector(_cmd));

To begin with, we make sure the context has not already been initialized to prevent accidentally destroying an existing context.

Next, we locate the model inside of our application bundle and verify that the NSManagedObjectModel initializes properly. This is an error that can only happen during testing and therefore is safe with just a ZAssert.


  NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
  ZAssert(coordinator, @"Failed to initialize coordinator");

  [self setManagedObjectContext:[[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]];

  [self setPrivateContext:[[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]];
  [[self privateContext] setPersistentStoreCoordinator:coordinator];
  [[self managedObjectContext] setParentContext:[self privateContext]];

We need to initialize the NSPersistentStoreCoordinator, verify that it initialized property, and then build our two NSManagedObjectContext instances. The NSPersistentStoreCoordinator should never fail with a proper NSManagedObjectModel, but I am checking just in case.

The first NSManagedObjectContext instance is defined with a NSMainQueueConcurrencyType and will be used by our User Interface (and therefore exposed outside of this controller). This is our Single Source Of Truth.

The second instance is defined as a NSPrivateQueueConcurrencyType. It is this second NSManagedObjectContext that receives a reference to the NSPersistentStoreCoordinator that we initialized previously. The main context then receives the private context as it’s parent.

We have created a full stack. The only thing we need now is an actual store on disk to access. This is where things get a little interesting.

The creation of the reference to the store can take time – an unknown amount of time. It can be nearly instantaneous (as is the case most of the time) or it can take multiple seconds if there is a migration or other issue. If it is going to take time, we do not want to block the main thread and therefore the User Interface.


  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    NSPersistentStoreCoordinator *psc = [[self privateContext] persistentStoreCoordinator];
    NSMutableDictionary *options = [NSMutableDictionary dictionary];
    options[NSMigratePersistentStoresAutomaticallyOption] = @YES;
    options[NSInferMappingModelAutomaticallyOption] = @YES;
    options[NSSQLitePragmasOption] = @{ @"journal_mode":@"DELETE" };

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];

    NSError *error = nil;
    ZAssert([psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error], @"Error initializing PSC: %@\n%@", [error localizedDescription], [error userInfo]);

To protect our main thread, we call -addPersistentStoreWithType: configuration: URL: options: error: in a dispatched background block. This will allow our -initializeCoreData method to return immediately, even if the persistent store needs to do some additional work. However, the user interface needs to know when it is safe to access the persistence layer. Therefore we need to use the callback block that was given to us.


    if (![self initCallback]) return;

    dispatch_sync(dispatch_get_main_queue(), ^{
      [self initCallback]();
    });
  });
}

Assuming that the developer gave us a callback block, we then execute it on the main thread. This can probably be called asynchronously but I am a ‘belt and suspenders’ type of guy when it comes to this stuff.

Our save method is also a little more complex because of the added context:


- (void)save;
{
  if (![[self privateContext] hasChanges] && ![[self managedObjectContext] hasChanges]) return;

  [[self managedObjectContext] performBlockAndWait:^{
    NSError *error = nil;

    ZAssert([[self managedObjectContext] save:&error], @"Failed to save main context: %@\n%@", [error localizedDescription], [error userInfo]);

    [[self privateContext] performBlock:^{
      NSError *privateError = nil;
      ZAssert([[self privateContext] save:&privateError], @"Error saving private context: %@\n%@", [privateError localizedDescription], [privateError userInfo]);
    }];
  }];
}

We check to see if we need to save. If neither context has any changes then there is no point in executing a save at all. It would just be a waste of cycles.

Since we cannot guarantee that caller is the main thread, we use -performBlockAndWait: against the main context to insure we are talking to it on its own terms. Once the main context has saved then we move on to the private queue. This queue can be asynchronous without any issues so we call -performBlock: on it and then call save.

The Application Delegate

This changes and cleans up the Application Delegate quite a bit. Now, instead of having a large amount of Core Data code spread through the Application Delegate and leading to a huge file, we have a very simple and clean object.


@class MCPersistenceController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
  @property (strong, nonatomic) UIWindow *window;
  @property (strong, readonly) MCPersistenceController *persistenceController;
@end

We have the required reference to the window and then we have a reference to the -persistenceController. Not a lot of noise in the interface.

Note that I made the -persistenceController read only. Only the Application Delegate has the right to destroy it.


#import "AppDelegate.h"
#import "MCPersistenceController.h"

@interface AppDelegate ()
  @property (strong, readwrite) MCPersistenceController *persistenceController;
  - (void)completeUserInterface;
@end

In the class continuation we allow the Application Delegate to write to the -persistenceController and we define the method that our callback is going to be using.


@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [self setPersistenceController:[[MCPersistenceController alloc] initWithCallback:^{
    [self completeUserInterface];
  }]];
  return YES;
}

Now our -application: didFinishLaunchingWithOptions: becomes a fair amount cleaner. We create the MCPersistenceController and not much else. Depending on the design of the application we may put a temporary UI here.


- (void)applicationWillResignActive:(UIApplication *)application
{
  [[self persistenceController] save];
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
  [[self persistenceController] save];
}

- (void)applicationWillTerminate:(UIApplication *)application
{
  [[self persistenceController] save];
}

As a rule, any time we are leaving the application we want to call save on the persistence controller. This guarantees that if we get killed while paused, we will not be losing data.

In most cases this is the only place you need to call save on the main and private contexts.


- (void)completeUserInterface
{
  //Application code goes here
}

Finally, we decide where the rest of the User Interface code goes. If we had a temporary user interface set up to handle migrations, etc., then we would be switching it out here for the full interface and continuing on.

Dependency Injection

Any time you write code like this:


id applicationDelegate = [[UIApplication sharedApplication] applicationDelegate];
NSManagedObjectContext *moc = [[applicationDelegate persistenceController] managedObjectContext];

You are doing yourself a disservice. Stop doing it!

The proper way to get access to the persistence controller is to inject it into each view controller as they are built and accessed. There are a number of reasons for this that are beyond the scope of this article. If you are interested in why, then I suggest reading up on the subjects of global variables and singletons. When you access the application delegate in this way you are effectively promoting its properties to the status of global variables and singletons.

Where To Go From Here

This article leaves out a number of bells and whistles in an attempt to keep it as succinct as possible.

For example, I generally ask for background processing time during the save of the contexts. This helps ensure that my save will be completed before the application is destroyed.

I would also expand upon the error handling in the event of a failure of the store to migrate. This is a rare situation (since we test completely, right?) and the error handling often gets overlooked or done in a very lazy fashion. This is because it is so rare that it should be given a lot of attention and will most likely be the subject of another article.

The Edges And Performance

This Core Data stack will work most of the time. Most is way more than 80%, but less than 100%. If you are relatively new to Core Data, this will suit you just fine for a long time.

Where does it not work? The occasions where I don’t use this stack usually involve large data manipulation. When I need to process a tremendous amount of data into the Core Data stack and that data can be isolated away from the User Interface.

If you are building a document based application, then your stacks are going to be different than this since you will have more than one.

The mantra is the same. Keep the code simple. Do not be clever, do not be lazy. Do it right and it will be easy to maintain. Be clever or lazy and you are only punishing yourself and your users.

This design pattern will not give you the absolute best performance possible.

Core Data is not the fastest persistence engine. That is not its purpose. If you need absolute best performance then you should consider writing to SQLite directly.

Core Data is about ease of use, consistence, and maintainability. It is a consistent interface to your application that promotes maintainability across developers. Use it correctly and any competent Objective-C developer will be able to follow your code with ease.

Keep It Simple

The mantra is the same. Keep the code simple. Do not be clever, do not be lazy. Do it right and it will be easy to maintain. Be clever or lazy and you are only punishing yourself and your users.

Marcus Zarra

MartianCraft Alumni

MartianCraft is a US-based mobile software development agency. For nearly two decades, we have been building world-class and award-winning mobile apps for all types of businesses. We would love to create a custom software solution that meets your specific needs. Let's get in touch.