Creating the Core Data Stack

This chapter shows you how to create and configure the Core Data stack, from the managed object context to the underlying persistent store.

Overview

The managed object context is responsible for managing a graph of managed objects. The task of managing the persistent stores falls to the persistent store coordinator. Its job is to mediate between the managed object context or contexts and the persistent store or stores. It presents a façade to the contexts, representing a collection of stores as a single virtual store. In this example, the coordinator manages just a single store.

To add a store, you use the NSPersistentStoreCoordinator method addPersistentStoreWithType:configuration:URL:options:error:. This returns an object representing the new store (or nil if it cannot be created). You must specify both the store’s location in the file system and its type (this example does not make use of model configurations). In this example it is an XML store—because its reasonably human-readable form facilitates testing. The file name extension is not .xml. You should avoid using generic file extensions—consider what would happen if all applications used the same extension…

The managedObjectContext Function

The managedObjectContext function returns a fully configured managed object context. It also creates and configures the remainder of the Core Data stack if necessary.

The managedObjectContext function is used in the main function, but implemented after the main function; you should therefore provide a forward declaration of the managedObjectContext function.

bullet
To declare the managedObjectContext function
  • At the top of the main source file, before main add a declaration for the function.

    NSManagedObjectContext *managedObjectContext();

The next step is to declare and create a stub implementation of the managedObjectContext function. The function should determine whether the managed object context instance already exists. If it does, simply return it, if it doesn’t, create it and then configure the remainder of the stack.

bullet
To begin the implementation of the managedObjectContext function
  • In the main source file, begin the implementation of the managedObjectContext function.

    In the implementation, declare a static variable for the context. If the variable is not nil return it immediately. If it is nil, create a new context, then return it as the function result.

    NSManagedObjectContext *managedObjectContext()
    {
        static NSManagedObjectContext *moc = nil;
        if (moc != nil) {
            return moc;
        }
     
        // Implementation continues...
     
        return moc;
    }

You put the code from the following steps immediately before the return statement (where the comment states, “Implementation continues...”).

The next step is to create the persistent store coordinator and configure the persistent store.

bullet
To set up the persistent store coordinator and store
  1. Create a persistent store coordinator.

    You initialize a persistent store coordinator using initWithManagedObjectModel:. The model specifies the schema of the stores the coordinator may manage. You tell the managed object context which persistent store coordinator it should use with setPersistentStoreCoordinator:.

    NSPersistentStoreCoordinator *coordinator =
        [[NSPersistentStoreCoordinator alloc]
            initWithManagedObjectModel:managedObjectModel()];
  2. Create a new persistent store of the appropriate type.

    You add a store to the persistent store coordinator using addPersistentStoreWithType:configuration:URL:options:error:. You use this method to specify:

    • The type of the store (for example, XML or SQLite).

    • The configuration to use from the persistent store coordinator’s managed object model.

      For this simple example, there are no configurations.

    • The URL of the store.

    • Store options, such as whether—if it already exists—the store should be updated to use a new model.

      Updating a store to use a new version of a model is referred to as migration—see Core Data Model Versioning and Data Migration Programming Guide.

      For this simple example, don’t specify any options.

    • A pointer to an error object.

    The method returns the new store, or nil if it could not be created. If for some reason the store cannot be created, log an appropriate warning.

    NSString *STORE_TYPE = NSXMLStoreType;
    NSString *STORE_FILENAME = @"CDCLI.cdcli";
     
    NSError *error;
    NSURL *url = [applicationLogDirectory() URLByAppendingPathComponent:STORE_FILENAME];
     
    NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE
                                               configuration:nil
                                               URL:url
                                               options:nil
                                               error:&error];
     
    if (newStore == nil) {
        NSLog(@"Store Configuration Failure\n%@",
            ([error localizedDescription] != nil) ?
            [error localizedDescription] : @"Unknown Error");
    }

Finally, create the context and associate it with the persistent store coordinator.

bullet
To create the managed object context
  1. Create the context.

    When you create a context (using initWithConcurrencyType:), you specify its concurrency type; the concurrency type tells the context which process queue it is intended to run on. To ensure that the context is only ever used on the main queue, specify NSMainQueueConcurrencyType

    moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
  2. Associate the persistent store coordinator with the context.

    [moc setPersistentStoreCoordinator:coordinator];

Build and Test

So that you can test the implementation thus far, instantiate the managed object context.

bullet
To test your implementation so far
  1. Instantiate the managed object context.

    In the main function, after the line in which the description of the managed object model is logged, declare a variable of type NSManagedObjectContext and assign its value to the result of invoking the managedObjectContext function.

    NSManagedObjectContext *moc = managedObjectContext();
  2. Build and run the project.

    The project should compile without errors, although you should get a warning that the variable moc is unused in the main function. When you run the utility, the managedObjectContext function should not log any errors.

Complete Listing

The complete listing of the managedObjectContext function is shown in Listing 4-1.

Listing 4-1  Complete listing of the managedObjectContext function

NSManagedObjectContext *managedObjectContext()
{
    static NSManagedObjectContext *moc = nil;
 
    if (moc != nil) {
        return moc;
    }
 
    NSPersistentStoreCoordinator *coordinator =
        [[NSPersistentStoreCoordinator alloc]
            initWithManagedObjectModel: managedObjectModel()];
 
    NSString *STORE_TYPE = NSXMLStoreType;
    NSString *STORE_FILENAME = @"CDCLI.cdcli";
 
    NSError *error;
    NSURL *url = [applicationLogDirectory() URLByAppendingPathComponent:STORE_FILENAME];
 
    NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE
                                               configuration:nil URL:url options:nil
                                               error:&error];
 
    if (newStore == nil) {
 
        NSLog(@"Store Configuration Failure\n%@",
              ([error localizedDescription] != nil) ?
              [error localizedDescription] : @"Unknown Error");
    }
 
 
    moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [moc setPersistentStoreCoordinator:coordinator];
 
    return moc;
}