This is the second part in a series on using core data with a full working app. In part 1 of the series we just set up the user interface by starting with a Tab Bar based application. In this part we will add core data to the tab bar app and save some user data.

If you need the code from part one you can get it here..
1.) Open up the app we set up in the last part of the tutorial. You should be able to see each view when the tab is selected and also select the save data button and see it print a line to the console. Now we’ll work on adding Core Data to it.
2.) Right click on Frameworks and add the CoreData.framework to the project by selecting Add > Existing Frameworks … and selecting CoreData.framework.
3.) Right click on Resources and select Add > New File. Choose Resource and then Data Model.

Name it CoreDataTabBarTutorial and click next,

on the last screen don’t select anything and just hit Finish.

4.) You can double click our new file (CoreDataTabBarTutorial.xcdatamodel) to open up the data model editor. In the top left (Entity) pane

click the little plus sign in the bottom left hand corner. Now you can edit the Entity in the top right pane.

Name the entity Destination.
Now click the little plus sign in the bottom left hand corner of the top middle (Property) pane.

and select Add Attribute.

The top right pane changes to allow you to edit the attribute. Name the attribute name and set it’s type to String.

Add two more attributes, location and date, make each of them String type.
5.) Now we’re going to create the custom class to represent our entity
Select the Destination entity in the model editor.

Then select File > New File from the menu bar and select Managed Object Class,

it should already know the location to out the new class,

select the Destination entity, make sure “Generate accessors” and” Generate Obj-C 2.0 Properties” options are checked and click Finish.

After creating this class you need to save the model (CoreDataTabBarTutorial.xcdatamodel) again.
Here’s what we have when we’re done.

6.) Expand Other Sources folder in the Groups & Files pane and select CoreDataTabBarTutorial_Prefix.pch. We’re going to add an import statement for CoreData here so that we won’t have to import it into all our individual files.
#ifdef __OBJC__
#import
#import
#import
#endif
7.) It’s time to add all the necessary code to the app delegate. Start by opening up the header file and adding the private variables for our ManagedObjectContext, ManagedObjectModel and PersistentStoreCoordinator. These are all the variables needed for core data. We also are adding two methods that we’ll implement, applicationDocumentsDirectory and saveContext.
#import@interface CoreDataTabBarTutorialAppDelegate : NSObject { UIWindow *window; UITabBarController *tabBarController; @private NSManagedObjectContext *managedObjectContext; NSManagedObjectModel *managedObjectModel; NSPersistentStoreCoordinator *persistentStoreCoordinator; } @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet UITabBarController *tabBarController; @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel; @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator; - (NSURL *)applicationDocumentsDirectory; - (void)saveContext; @end
Next open up the implementation file for the app delegate and let’s work in it. We won’t be synthesizing the variables because we’re going to write our own accessor methods. But first let’s implement the two methods we declared in the header file.
The applicationDocumentsDirectory which gets us the location of the documents directory in our app.
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
And the saveContext method which saves our context when exiting the app.
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *objectContext = self.managedObjectContext;
if (objectContext != nil)
{
if ([objectContext hasChanges] && ![objectContext save:&error])
{
// add error handling here
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
}
Here are the accessor methods.
#pragma mark -
#pragma mark Core Data stack
/**
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 from the application's model.
*/
- (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 = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataTabBarTutorial.sqlite"];
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return persistentStoreCoordinator;
}
8.) In our EnterDataView we’re going to save the data that a user enters so we need to access the core data from the EnterDataViewController class. Open up the header file and we’ll add instance variables for FetchedResultsController and ManagedObjectContext.
#import@interface EnterDataViewController : UIViewController { UITextField *nameTextField; UITextField *locationTextField; UITextField *dateTextField; NSFetchedResultsController *fetchedResultsController; NSManagedObjectContext *managedObjectContext; } @property (nonatomic, retain) IBOutlet UITextField *nameTextField; @property (nonatomic, retain) IBOutlet UITextField *locationTextField; @property (nonatomic, retain) IBOutlet UITextField *dateTextField; @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; - (IBAction) saveData; - (void) dismissKeyboard; @end
Now open the implementation file and synthesize the variables,
@synthesize fetchedResultsController, managedObjectContext;
then release then in dealloc,
- (void)dealloc
{
[fetchedResultsController release];
[managedObjectContext release];
[nameTextField release];
[locationTextField release];
[dateTextField release];
[super dealloc];
}
9.) Go back to EnterDataViewController and implement our saveData method, but first import Destination into our implementation file,
#import "EnterDataViewController.h" #import "Destination.h" @implementation EnterDataViewController @synthesize nameTextField, locationTextField, dateTextField; @synthesize fetchedResultsController, managedObjectContext;
and then we can implement the method.
You generally create a managed object using a convenience method, we are going to use -insertNewObjectForEntityForName:inManagedObjectContext:. This returns an initialized instance of the class for the entity provided, in our case Destination.
- (IBAction) saveData
{
NSLog(@"saveData");
[self dismissKeyboard];
Destination *dest = (Destination *)[NSEntityDescription insertNewObjectForEntityForName:@"Destination" inManagedObjectContext:managedObjectContext];
dest.name = nameTextField.text;
dest.location = locationTextField.text;
dest.date = dateTextField.text;
NSError *error;
// here's where the actual save happens, and if it doesn't we print something out to the console
if (![managedObjectContext save:&error])
{
NSLog(@"Problem saving: %@", [error localizedDescription]);
}
// **** log objects currently in database ****
// create fetch object, this object fetch's the objects out of the database
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Destination" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *fetchedObjects = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (NSManagedObject *info in fetchedObjects)
{
NSLog(@"Destination name: %@", [info valueForKey:@"name"]);
NSLog(@"Destination location: %@", [info valueForKey:@"location"]);
NSLog(@"Destination date: %@", [info valueForKey:@"date"]);
}
[fetchRequest release];
}
10.) We’ve implemented the necessary code in EnterDataViewController, but as of right now it doesn’t have a reference to the ManagedObjectContext that we set in the app delegate. To solve that we are going to create a reference to the view controller in the app delegate and set the ManagedObjectContext in the view controller with the one in our app delegate. We’ll do this for all three view controllers while we’re at it.
Open up the app delegate header file, import the view controllers and declare instance variables for them.
#import#import "EnterDataViewController.h" #import "ShowDestinationsViewController.h" #import "SearchDestinationsViewController.h" @interface CoreDataTabBarTutorialAppDelegate : NSObject { UIWindow *window; UITabBarController *tabBarController; EnterDataViewController *enterDataViewController; ShowDestinationsViewController *showDestinationsViewController; SearchDestinationsViewController *searchDestinationsViewController; @private NSManagedObjectContext *managedObjectContext; NSManagedObjectModel *managedObjectModel; NSPersistentStoreCoordinator *persistentStoreCoordinator; } @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet UITabBarController *tabBarController; @property (nonatomic, retain) IBOutlet EnterDataViewController *enterDataViewController; @property (nonatomic, retain) IBOutlet ShowDestinationsViewController *showDestinationsViewController; @property (nonatomic, retain) IBOutlet SearchDestinationsViewController *searchDestinationsViewController; @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel; @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator; - (NSURL *)applicationDocumentsDirectory; - (void)saveContext; @end
Switch to the implementation file and synthesize the variables,
#import "CoreDataTabBarTutorialAppDelegate.h" @implementation CoreDataTabBarTutorialAppDelegate @synthesize window; @synthesize tabBarController; @synthesize enterDataViewController, showDestinationsViewController, searchDestinationsViewController;
And then we will set the ManagedObjectContext for them in the application:didFinishLaunchingWithOptions method. Because we haven’t yet added the ManagedObjectContext to the showDestinationsViewController or the searchDestinationsViewController we’ll comment out those two lines for now so we don’t’ get any errors.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
enterDataViewController.managedObjectContext = self.managedObjectContext;
// showDestinationsViewController.managedObjectContext = self.managedObjectContext;
// searchDestinationsViewController.managedObjectContext = self.managedObjectContext;
// Add the tab bar controller's view to the window and display.
[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];
return YES;
}
11.) Now here’s the real trick with adding Core Data to a Tab Bar app. We need to hook up those view controllers we created in the app delegate header file to the view controllers used in Interface Builder. You may have noticed that we set the properties on the instance variables as IBOutlet so that we could do just that. So open up MainWindow.xib in Interface Builder.
In the documents window, expand Tab Bar Controller. Then control click/drag from the app delegate to each view controller and select the appropriate IBOutlet.

12.) One last thing to clean up. Open EnterDataView.xib and set the delegate for each text field to the file’s owner. We need this so that when a user hits the return button on the keyboard, the keyboard will go away.
13.) That’s it try doing a Build and Run and entering some data. When you hit the save button you’ll see print lines in the the console for every destination you have entered.

That’s all we’ll do for this part of the tutorial. In the next part we’ll fetch the core data from a different view and display the data in a table view.
Hi Kent,
Thank you very much for a great tutorial. It helped a great deal in understanding how to add CoreData to my App with a UITabbarController.
Unfortunately, it does not work (yet). It compiles ok, and when I run it in the simulator, the App starts but then shows a black screen. The App seems to be running, judging from the Console and the “GDB Running…” message at the bottom, but nothing happens. I tried putting NSLog() messages in at several places, but I don’t see any of them; not even the one at the top of the didFinishLaunchingWithOptions method in my AppDelegate. (Actually, I did see the one in main.m just before the call to UIApplicationMain, but nothing after that.) My App worked ok before I added the CoreData stuff.
Any idea what can be wrong?
Thank you very much in advance.
Kind regards,
–Gerard Broeksteeg
Eindhoven, The Netherlands
Hard to say what the problem is with out seeing the code. Can you put a break point in the app delegate application:didFinishLaunchingWithOptions method and see if it get’s there?
Hi Kent,
Thanks for the reply. I found that the problem had nothing to do with CoreData, but with a missing link in MainWindow.xib.
Tx!
–Gerard
Hey Kent, may I know how do I go about adding a navigation bar at the “Show Destination” tab? I tried doing so in the IB but it didn’t appear.
By the way, great tutorial! Really hit the spot for many of my questions.
Nat are you using xcode 4? If so this will help you
http://www.youtube.com/theappcodeblog#p/a/u/1/aSBK8Zz6O4M
If you are not using xcode 4 let me know.
Thanks,
Kent
Hey that’s it! Thanks alot! =)
You’re a real lifesaver!
Your tutorial have really been helpful in understanding core data
Thank you * infinity.
Can u plz do a tutorial abt relationships in Coredata cauz i m badly stuck in my project
Your particularly well organized, very practical, & very well thought-out tutorials plus the Hegarty/Stanford iOS course have brought me up-to-speed very quickly. Thanks for the time and effort to provide this info.
Pingback: beginner iOS core data – model is nil
Can you post a tutorial that has two views (using tab bar controller). The first view takes strings from the user and saves it in sqlite database and the second view retrieves them from the database in table form? Please post tutorials that use storyboard.
Hi Kent, great set of tutorials. I just want to check; If I do this method and release an app…is the data retained when the user updates the app? Thankyou!
also, I have done the tutorial for my own project which uses storyboards. Do you know how to do stage 11 in storyboards?
Yes data should be retained.