Moving Core Data to the Singleton

[UPDATE:] You might like to read Revisiting the Singleton to see how my opinion has changed on this.

Now we’ve established the principle of using a Singleton as our main app coordinator, it would make more sense to move the Core Data functions there.  If you choose to add Core Data when generating from an XCode template, these function are placed in the AppDelegate class.  Not so much use to me!

Add Core Data methods to the Singleton @interface (.h)

[cc lang='objc' width='auto' highlight='2,3,4,8']
@interface MyApp : NSObject {
    NSManagedObjectModel *managedObjectModel;
    NSManagedObjectContext *managedObjectContext;
    NSPersistentStoreCoordinator *persistentStoreCoordinator;
}

+ (MyApp *) singleton;
- (NSString *)applicationDocumentsDirectory;
[/cc]

The above establishes that instance variables and accessor methods that will be used.  Note that there are no @property directives at this point.  XCode chooses to implement the getters for these instance variables in a particular way, and its not immediately clear why this is.  However, here’s an explanation.

@property enables dot notation, @synthesize implements undefined getters and setters

The @property directive enables the use of the dot notation (i.e. [cci]self.managedObjectContext[/cci]).  Without use of the @property directive, you’d have to stick to [cci][myObject managedObjectContext][/cci] notation.

The @synthesize directive implements getters and setters where they haven’t been otherwise defined. @synthesize however is NOT mandatory, so use of the @property directive on its own does enable dot notation.

Categories Hides the Getters from Callers

The @property statements are contained in the .m file, just before the @implementation statement, as part of a category called ‘PrivateCoreDataStack’.  Categories are a way of extending the definition of a class, without the need for subclassing.  By implementing these getters within the .m file, they can only be seen by the ‘MyApp’ class.  So only MyApp can see the instance variables (as they are ‘protected’ by default) and no external caller knows about the getters.  This helps to keep these variables private to the implementing class, and so their values cannot be changed by outside influence.

Enable dot notation via categories, private to the MyApp class

[cc lang='objc' width='auto']
@interface MyApp (PrivateCoreDataStack)
@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@end
[/cc]

Implement Core Data methods

The Core Data methods are now added to the .m file (MyApp.m).  Note that these are implemented through the getters in such a way that they are automatically allocated on first use.  This saves on having to provide specific setup for them elsewhere in the application.

[cc lang='objc' width='auto']
/**
 Returns the managed object context for the application.
 If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
 */
- (NSManagedObjectContext *) managedObjectContext {

    if (managedObjectContext != nil) {
        return managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator: coordinator];
    }
    return managedObjectContext;
}

/**
 Returns the managed object model for the application.
 If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
 */
- (NSManagedObjectModel *)managedObjectModel {

    if (managedObjectModel != nil) {
        return managedObjectModel;
    }
    managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
    return managedObjectModel;
}

/**
 Returns the persistent store coordinator for the application.
 If the coordinator doesn't already exist, it is created and the application's store added to it.
 */
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

    if (persistentStoreCoordinator != nil) {
        return persistentStoreCoordinator;
    }

    NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"MyApp.sqlite"]];

	NSError *error = nil;
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
		/*
		 Replace this implementation with code to handle the error appropriately.

		 abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.

		 Typical reasons for an error here include:
		 * The persistent store is not accessible
		 * The schema for the persistent store is incompatible with current managed object model
		 Check the error message to determine what the actual problem was.
		 */
		NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
		abort();
    }

    return persistentStoreCoordinator;
}

#pragma mark -
#pragma mark Application's Documents directory

/**
 Returns the path to the application's Documents directory.
 */
- (NSString *)applicationDocumentsDirectory {
	return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
[/cc]

Note that the [cci]applicationDocumentsDirectory[/cci] method provides a useful function to the Documents folder within the iPhone Sandbox, but the actual filename is provided in the [cci]persistentStoreCoordinator[/cci] method.

1 thought on “Moving Core Data to the Singleton

Leave a Reply

Your email address will not be published. Required fields are marked *