Adding Core Data Existing iPhone Projects

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 Core 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

Add Links and Use Core Data

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.

  • http://www.mick-walker.co.uk Mick Walker

    Thanks for writing this up; just a quick note:

    AppDelegate.h also needs to following:
    – (NSString *)applicationDocumentsDirectory;

    Regards
    Mick

  • http://www.wiresareobsolete.com Dave

    Mick –

    Thanks for the note! I originally omitted the documents directory property because it’s really more of a convenience method than a required piece of the Core Data structure.

    However, I reread the post and was caught red-handed using the property in another method! So I’ve edited the post to include its definition.

  • http://www.tigelane.com Tige

    Dave,
    Thank you very much for the easy to follow writeup on how to do this. I’m sure it saved countless hours of time either figuring it out myself, or starting with a new project. I found one problem when I went to compile after the add and thought I would help others out that might run into this.

    I received error: Expected specifier-qualifier-list before ‘NSManagedObjectModel’

    This was solved by adding “#import ” to the project _Prefix.pch file. There are several internet hits if you google the error.

    Thanks Dave!

  • http://www.wiresareobsolete.com Dave

    Thanks for that note. #import <CoreData/CoreData.h> should definitely be in either the _Prefix.pch file for the project, or the .h files for the classes using Core Data. Should have mentioned that!

  • mgw

    This was just what I was looking for! Thanks for the simple instructions.

  • tobe

    thanks for the tutorial and the comments. It helped me a lot

  • http://iamconor.wordpress.com Conor Griffin

    Hey Dave, great post. I’ve followed your instructions and I have run into a problem which I can’t figure out the answer to. Maybe it will be obvious to you.

    As you recommended, I placed the line

    ViewController.managedObjectContext = self.managedObjectContext;

    in the ApplicationDidFinishLaunching method of my Delegate, but I get the following error:

    “Accessing unknown ‘setManagedObjectContext:’ class method”
    and
    Object cannot be set – either readonly property or no setter found

    If you’ve any idea what is likely to cause this I’d be very grateful for your help, thanks

  • http://iamconor.wordpress.com Conor Griffin

    I should say that my app is a UITabBarController containing 3 separate view controllers

  • http://www.wiresareobsolete.com Dave

    Hi Conor –

    Those errors are referring to the managedObjectContext member of your View Controller; and I have two ideas:

    1. Make sure that @synthesize managedObjectContext is in your ViewController.m file. This is the call that creates the setter methods, and may have been missed.

    2. Make sure that when you defined managedObjectContext in the ViewController.h with @property that it wasn’t defined with a “readonly” attribute. I mention this because the object in AppDelegate IS defined as readonly, so there may be confusion there.

    Hope one of those helps!

  • http://iamconor.wordpress.com Conor Griffin

    Thanks Dave, I found the error though. As I’m still learning I had made a rookie mistake, but it might be useful for you to include in your instructions in case someone else does the same.

    I had defined my ViewController with an @Class declaration in the AppDelegate.h file, but I had not instantiated it. I was also missing the #import statement for my ViewController.h file at the beginning of my AppDelegate.m file.

    So, when I tried to reference the ViewController.managedObjectContext as you described, the controller threw an error because it didn’t know anything about it until I instantiated the class and imported its header file.

    Was a tough bug to find but I think I’m good to go now, at least I’m compiling cleanly, we’ll see how it goes once I start calling on objects.

    Thanks again ;o)

  • Bruno Watt

    Brilliant! I was considering taking up smoking again to deal with working out what was needed. ;P

    Thanks heaps Dave and all contributors here who saved me much agony!

  • http://none David

    Very good, well done!

  • Normann

    thanx alot!

  • Prasanna

    its cool man… tons of thanks

  • Pingback: core wires

  • Ted W. Bendixson

    Thank you!

    Pretty much everything I’ve seen on this topic has suggested creating a new project from scratch and just dealing with the hassle of moving everything over. This was very helpful.

  • Ruslan Ardashev

    Great post!

    I think a better way of using the AppDelegate.managedObjectContext variable would be to have a “sharedInstance” class variable inside AppDelegate that other instances reference. That way, you don’t need to worry about modifying your AppDelegate every time you add another class / instance that needs to use the managedObjectContext.

    // Inside ViewController
    [[AppDelegate] sharedManagedObjectContext] myMethod];