iPhone Development Tutorial – add core data to a Tab Bar application and save user data.

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.

Destination 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.

Add a data model

Name it CoreDataTabBarTutorial and click next,

Name data model

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

Finish adding data model

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

Entity pane

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

Edit entity

Name the entity Destination.

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

Property pane

and select Add Attribute.

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.

Edit attribute

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.

Select Destination Entity

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

Choose Managed Object Class

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

Check location and target

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

select Destination entity

After creating this class you need to save the model (CoreDataTabBarTutorial.xcdatamodel) again.

Here’s what we have when we’re done.

Finished data model editor

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.

Link view controllers to IBOutlets

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.

Simulator Shot

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.

Here’s the code..



This entry was posted in Data, Tab Bar and tagged , , , , , , , , , . Bookmark the permalink.

13 Responses to iPhone Development Tutorial – add core data to a Tab Bar application and save user data.

  1. Gerard Broeksteeg says:

    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

    • Kent says:

      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?

      • Gerard Broeksteeg says:

        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

  2. Nat says:

    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.

  3. Omer says:

    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

  4. --Nick/ says:

    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.

  5. Pingback: beginner iOS core data – model is nil

  6. Ingila says:

    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.

  7. Patrick says:

    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!

Add Comment Register



Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>