Introduction to Core Data: Powerful Persistent Data Storage in iOS

[box type=”shadow”]

Editor’s note:

After we published the tutorial about AFNetworking Crash Course,Some of the readers asked about the core data and how we can implement it in a live project in a perfect manner.So we decided to create a small project to demonstrate how core data can be included in real world applications. [/box]

This tutorial will teach you about persistance data storage on iPhone(this can be adapted in all iOS devices above iOS 5.0 ). There are lots of good book’s available in store which explain’s the detailed usage of core data and its functions. I think this is a good book “Pro Core Data for iOS” for learning core data for ios. if you wish you can take a look at it.In iPhone (iOS) app’s there are lots of ways to store data,then why we use core data? because it is so simple compared to conventional database.

The main intention of this tutorial is to provide a live project example of core data framework in iOS.Basically i am going to create a tableview based iOS application. i am assuming that you are familiar with UITableView and it’s implementation. In this tutorial i will not explain more about the UITableView delegates or its implementation. if you have any doubt in UITableView please refere the previous post done by me.

Core Data is not a Conventional Database

Generally people think about persistance data storage,they will come up with a solution by saying we can use database. If you are a experienced developer with some relational database experience like sql or mysql or oracle, we usually store data in table and store that in the database. After that we will retrive the data using sql quires. But Core Data is entirely different. Core Data is stored in SQLite database. Core Data is not at all a conventional relational database.Core Data is basically a framework, which is used to store and retrive data from a database in a object-oriented manner.With the help of Core Data we can easily map the objects without knowing any conventional database operations.

To get understand the Core Data Concept, we will prepare a sample app,So lets get started. We are going to create a small application to take the order inside a restaurant.It will just only take one item, its quantity and ordered table number.Let’s name the application as “My Restaurant”. Its an example app so i am not going to make any complex object mapping or anything.

Creating a Sample App with Core Data

So Let’s get start, First i am going to select Master-Detail Application template in xcode and name it as My Restaurant.Please take a look at the screenshot, so you can understand easily.

core data template selection

core data template selection

After that i just named the project “My Restaurant”. Dont forget to select the Core Data.

Core Data project naming

Adding Core Data Project Name

Architecture of Core Data

Before crack the code, i am just planing to give a small explanation about the architecture of core data project. If you are familiar with Core data layers then it will be easy to understand.Basically Core Data has 3 layers,they are

  • Managed Object Model.
  • Persistent Store Coordinator.
  • Managed Object Context.

1. Managed Object Model

[box type=”shadow”]
It will explains the schema that you uses in the application. That menas it represents a collection of object/entities. In Xcode, the managed object model is saved in a file and its belongs with an extension of .xcdatamodeld . Also in xcode we have visual editors to manage this.With the help of the visual editors we can add or edit entities, attributes and relationships.
[/box]

2. Persistent Store Coordinator

[box type=”shadow”]
In iOS apps SQLite is the persistance storage.Core data will help the developers to setup multiple stores containing different entities.This persistance Store Coordinator will help the developer to save the data to the store.Forget about it you don’t understand what it is.Because you are not directly working with this when you playing with Core Data. 😉
[/box]

3. Managed Object Context

[box type=”shadow”]
This will manage the Objects that communicate with the Core Data.When you dealing with Core Data, Managed Object Context is the one you’ll work with for most of the time.In general, whenever you need to fetch and save objects in persistent store, the context is the first component you’ll talk to.
[/box]

I think pictorial representation make you more clear !.

Core data architecture

Layers of Core Data

How to Defining Managed Object Model

Now you have to take a look at MyRestaurant.xcdatamodeld you need to create a new entity over there. to create a new Entity you need Click on the add Entity.

Creating Entities

How to Create Entities in Core Data

Now you need to add the attributes to the Entities,don’t forget to select the correct data types.

adding attributes

How to add attributes to Entities in Core Data

Designing the User Interface for the Project

For the time being i am not adding much designs to the user interface. Now i am only just adding some UITextBox and one UIButton to take the order in the details view.

Take Order View

Take Order View

Now i am going to work with MasterViewController.xib file, the main interface of the application which display the order’s list. In that navigation bar, we need to add + button to take the order and edit button to manage the order (Delete). For that we need to add the bellow code snippet in viewDidLoad.

							
- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *takeOrderBTN = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(takeOrder:)];
    self.navigationItem.rightBarButtonItem = takeOrderBTN;
}

Now we need to define the function takeOrder. when the user click on the + button it will navigate to the Take Order view (DetailViewController). To do that we need to add some code in to the takeOrder function.

- (void)takeOrder:(id)sender
{
    if (!self.detailViewController) {
        self.detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];

    }
    [email protected]"Take Order";
    self.detailViewController.managedObjectContext = self.managedObjectContext;
    [self.navigationController pushViewController:self.detailViewController animated:YES];

}

Now we need to Move to Core data.

Diving into Core Data

In app delegate we need to set the NSManagedObjectContext,NSManagedObjectModel,NSPersistentStoreCoordinator. so now you need to create the instance of the all there in the app delegate.
After that the appDelegate.h will be look like this

@interface AppDelegate : UIResponder 

@property (strong, nonatomic) UIWindow *window;

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

- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;

@property (strong, nonatomic) UINavigationController *navigationController;

@end

Next we need to @synthesize the properties in its delegate.

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

Next we need to define the Managed Object model

// 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;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyRestaurant" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

After that we need to prepare the persistentStoreCoordinator.

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

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"MyRestaurant.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;
}

Next we need to create managedObjectContext

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

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

finally you need to create a function to save context.

- (void)saveContext
{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&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. 
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        } 
    }
}

No worries about the long codes it will generate by itself when you select the Core Data in the Template itself.

Inserting Value to Core Data

For taking the order we need to create 3 IBOutlets of UITextField and wire it with according to the correct outlets.

    IBOutlet UITextField *item;
    IBOutlet UITextField *qty;
    IBOutlet UITextField *name;

Now we need to save the data to the Core Data.To do that you need to create a function SaveOrder and wire it with the save UIButton.

- (IBAction)SaveOrder:(id)sender
{

     NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
     NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
     NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];

     // If appropriate, configure the new managed object.
     // Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
     [newManagedObject setValue:item.text  forKey:@"item"];
     [newManagedObject setValue:name.text forKey:@"name"];
     [newManagedObject setValue:[NSNumber numberWithInteger:[qty.text integerValue]]forKey:@"qty"];

     // Save the context.
     NSError *error = nil;
     if (![context save:&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.
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
     abort();
     }

    [self.navigationController popToRootViewControllerAnimated:YES];

}

Also we need to create a fetchedResultsController to fetch the data from CoreData.

#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Order" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"item" ascending:NO];
    NSArray *sortDescriptors = @[sortDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Order"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

	NSError *error = nil;
	if (![self.fetchedResultsController performFetch:&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.
	    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
	    abort();
	}

    return _fetchedResultsController;
}
Taking the Order and saving the data to Core Data

Taking the Order

[box type=”info”] You can continue reading its part 2 :- Introduction to Core Data: Powerful Persistent Data Storage in iOS part2[/box]

Leave a Reply

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