Adding Core Data Existing iPhone Projects

Dave Smith
Dave Smith

Core Data is a very powerful framework that Apple provides to developers for persisting data in their applications. The primary advantage that is provided by Core Data is the ability to leverage efficient data storage technologies like SQLite, without forcing the developer to think in terms of query language; Core Data allows a developer to work with the data model in terms of objects.

There are many great books and articles on the proper use of Core Data; including Apple's Core Data Tutorial. The purpose of this article is simply to step the reader through adding the necessary piece to get Core Data into a previously created project; I defer to those other writings to describe the best techniques for Core Data use.

Apple has made the process of using Core Data in new projects very simple. From the New Project... screen, many project templates include a checkbox titled Use Core Data for storage that tells Xcode to build the basics for Core Data into the project you create.  But what happens if you decide that Core Data's advantages are going to help you, but you have already have your project set up in Xcode? Where's the button in the IDE to add Core Data to an existing project?

Below is a set of instructions on creating the context necessary to start using ore Data in your existing app.  In the code blocks, bold text is code that should already exist in your project.

Add the Missing Files

There are two files that must be added to your Xcode project in order to use Core Data; CoreData.framework and the .xcdatamodel

CoreData.framework

  1. From Xcode, control-click on the Frameworks folder inside the Groups & Files pane
  2. Select Add -> Existing Frameworks...
  3. Locate CoreData.framework, select it, and click the Add button

.xcdatamodel

  1. From Xcode, control-click on the Resources fold inside the Groups & Files pane
  2. Select Add -> New File...
  3. Select Resource from the iPhone OS group
  4. Select Data Model and click Next
  5. Give the file a name (the project name is a good choice) and click Next
  6. If your project does not have existing model classes that you would like to put into the data model, skip to step 7. Otherwise, you may do so by selecting those classes on this screen and clicking Add to place them in the Selected Classes pane.
  7. Click Finish

Create the Missing Objects

Once all the necessary files are put into the project, the appropriate objects and methods need to be added to the application.

AppDelegate.h

Declare three new objects in the application delegate's header file for the ManagedObjectModel, ManagedObjectContext, and PersistentStoreCoordinator. A convenience method, applicationDocumentsDirectory, is also defined to return the location of the application's data files:

@interface AppDelegate : NSObject <UIApplicationDelegate> {
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;

/* ...Existing Application Code... */
}

@property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (NSString *)applicationDocumentsDirectory;

/* ...Existing Application Code... */
@end

AppDelegate.m

Implement applicationDocumentsDirectory, and explicitly write accessor methods for each new property as opposed to simply using the @synthesize keyword.

Note in the persistentStoreCoordinator accessor there is a location where you must name the SQLite file used for the store; this should most likely be your project name.

Remember to properly release each object in dealloc:

@implementation AppDelegate
//Explicitly write Core Data accessors
- (NSManagedObjectContext *) managedObjectContext {
  if (managedObjectContext != nil) {
    return managedObjectContext;
  }
  NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
  if (coordinator != nil) {
    managedObjectContext = [[NSManagedObjectContext alloc] init];
    [managedObjectContext setPersistentStoreCoordinator: coordinator];
  }

  return managedObjectContext;
}

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

  return managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
  if (persistentStoreCoordinator != nil) {
    return persistentStoreCoordinator;
  }

  NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
      stringByAppendingPathComponent: @"<Project Name>.sqlite"]];
  NSError *error = nil;
  persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
      initWithManagedObjectModel:[self managedObjectModel]];
  if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
      configuration:nil URL:storeUrl options:nil error:&error]) {
    /* Error for store creation should be handled in here */
  }

  return persistentStoreCoordinator;
}

- (NSString *)applicationDocumentsDirectory {
  return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}

/* ...Existing Application Code... */

- (void)dealloc {
  [managedObjectContext release];
  [managedObjectModel release];
  [persistentStoreCoordinator release];

/* ...Existing Dealloc Releases... */
}
@end

ViewController.h

The following code needs to be added to the interface of whatever view controller will interact with the Core Data objects. The FetchedResultsController and another instance of the ManagedObjectContext:

@interface ViewController : UIViewController <NSFetchedResultsControllerDelegate> {
  NSFetchedResultsController *fetchedResultsController;
  NSManagedObjectContext *managedObjectContext;

  /* ...Existing Application Code... */
}

@property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
@end

ViewController.m

Don't forget to synthesize the new properties for their accessor methods:

@implementation ViewController
@synthesize fetchedResultsController, managedObjectContext;

/* ...Existing Application Code... */
@end

The managedObjectContext object has now been created as a property in both the AppDelegate and ViewController classes. The declaration of managedObjectContext in the ViewController must reference the AppDelegate, which is the only place that object should be allocated. Something like

ViewController.managedObjectContext = self.managedObjectContext;

should exist in the applicationDidFinishLaunching method of the AppDelegate to accomplish this.

Conclusion

Your project should now include all the necessary objects to perform Core Data operations within the application.  You can create the data model using Xcode's built-in editor to define entities, properties, and relationships. Then, work with the Core Data framework to store and fetch your persisted object data.