Tuesday, June 2, 2015

iOS Development : Core Data



This post explains basics of core data with one sample tutorial application development guidelines.


Before we start, lets first clear some common misconception about core data. 


So what exactly is Core Data ?



  • Core data is not any kind of database, don’t mix up Core Data with database.
  • It was introduced in Mac OS X 10.4 Tiger and iOS with iPhone SDK 3.0. (2009)
  • It is a framework provided by apple, which lets you perform operations on data in the database in an object-oriented way. 
  • Core Data is much more than just a framework to load and save data.



Why should i use Core Data ?





1. Suitable for quick schema setup and modification.



2. Less memory overhead as it uses lazy loading techniques. When you fetch data, actual objects are fetched only when you try to access them ( aka "faulting").


3. Model Editor Interface : You can use this cool editor interface provided inside Xcode itself to setup schema. Create tables, setup relationships, writing queries and much more..

4. Less code

  •  No need to write create table queries.
  •  No need to write programmatic fetch queries.
  •  Fetched data is already in the form of model classes you need, plus you can save data using model classes directly. So layer which performs fetching and converting data into required model class is removed.

5. Easy to migrate to new schema, if you have proper initial code setup then you can perform light weight migration without writing single line of code.

6. Easy to visualise schema using Model Editor Interface




Basics of Core Data



The learning curve for Core Data is quite large. The goal of this post is to get you up to speed with the basics of Core Data quickly by very short description and small tutorial application.


The Stack


Traditional setup :


To create database and perform any operations on it you need,

1. Schema / design for your database
2. Database connection / handle
3. Custom layer providing methods to interact with database, which performs operations to provide you data received from DB in required model format, also saves your model objects into database in required format.


Core Data setup :


Core data provides similar stack with some complex looking terms at first glance,


1. Managed Object Model

This is your schema, you can design it inside Xcode itself, Xcode provides cool interface for it.
It removes overhead of creating tables programmatically. Apart from just setting up tables and relationships it also provides many other features.
File extension for this is .xcdatamodeld.


2. Persistent Store Coordinator

This is something similar to database connection / handle.


3. Managed Object Context 

Think of it as a “scratch pad” containing objects that interacts with data in persistent store. Its job is to manage objects created and returned using Core Data.




Tutorial


#Download :

Link Source Download : Core Data Tutorial


#Goal :

Create simple Contact table with attributes 
a. name 
b. phone number
c. email

Programatically add some contacts.

Fetch added contacts and print on console.



#Steps :

0. Create single view application, all coding should be performed in ViewController.m file


1. Import Core Data Framework header


#import <CoreData/CoreData.h>


2.  Declare core data stack essential properties


@property (strong, nonatomic) NSManagedObjectContext        *managedObjectContext;

@property (strong, nonatomic) NSManagedObjectModel          *managedObjectModel;

@property (strong, nonatomic) NSPersistentStoreCoordinator  *persistentStoreCoordinator;


3. Copy all following methods in pragmas "Core Data Stack & Core Data Save" as it is, these are common methods required work with core data. Each method has comments for code understanding purpose.




#pragma mark - Core Data Stack

- (NSManagedObjectModel *)managedObjectModel
{
    //1. Create only if nil
    if (_managedObjectModel != nil)
        return _managedObjectModel;
    
    //2. Set schema file name ( .xcdatamodeld file)
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreData" withExtension:@"momd"];
    
    //3. Initialize managedObjectModel with url
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    //1. Create only if nil
    if (_persistentStoreCoordinator != nil)
        return _persistentStoreCoordinator;
    
    //2. Init with ManagedObjectModel
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    
    //3. Create .sqlite file path url
    NSURL *documentDirPath = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
    NSURL *storeURL = [documentDirPath URLByAppendingPathComponent:@"CoreData.sqlite"];
    
    //4. Add SQLLite as persistent store using .sqlite file url
    NSError *error = nil;
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
    {
        NSLog(@"Failed to initialize persistent store");
    }
    
    return _persistentStoreCoordinator;
}


- (NSManagedObjectContext *)managedObjectContext
{
    //1. Create only if nil
    if (_managedObjectContext != nil)
        return _managedObjectContext;
    
    //2. Get store coordinator
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator)
        return nil;
    
    //3. Initialize managedObjectContext & set perstent store for managedObjectContext
    _managedObjectContext = [[NSManagedObjectContext alloc] init];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    
    return _managedObjectContext;
}

#pragma mark - Core Data Save

- (void)saveContext
{
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    
    if (managedObjectContext != nil)
    {
        NSError *error = nil;
        
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error])//save only if there are changes by checking "hasChanges"
        {
            NSLog(@"Failed to save context data");
        }
    }


}

4. Setup Schema using Model Editor Interface


4a. Add new file to project

    add new "Data Model" file from "Core Data" group name it as "CoreData".
    IMPORTANT : 
    No other name will work as we have set this name in code in method managedObjectModel.




4b. This is how empty model file looks..




4c. Adding "Contact" entity and "name" attribute



4d. Use steps provided in 4c. image to add remaining attributes "email" and "phoneNumber". This is how final schema will look..




5. Create "Contact" class as subclass of NSManagedObject using Model Editor Interface
   Select entity "Contact" > Go to Editor > Create NSManagedObject Subclass

   This will add Contact.h & Contact.m file to project with attributes provided in schema as property names with appropriate data types.




6.  Import NSManagedObject subclass "Contact" to "ViewController.m"

#import "Contact.h"

7. Copy methods addContacts and fetchContacts as it is in code..


#pragma mark - Add and Fetch Contacts

-(void)addContacts
{
    //1. Get managedObjectContext
    NSManagedObjectContext *context = [self managedObjectContext];
    
    //2. Create Entity with name and context
    Contact *contact1 = [NSEntityDescription
                         insertNewObjectForEntityForName:@"Contact"
                         inManagedObjectContext:context];
    
    //3. Set attribute values
    contact1.name = @"aaaa";
    contact1.email = @"aaaa@aaaa.com";
    contact1.phoneNumber = [NSNumber numberWithInt:1111];
    
    Contact *contact2 = [NSEntityDescription
                         insertNewObjectForEntityForName:@"Contact"
                         inManagedObjectContext:context];
    
    contact2.name = @"bbbb";
    contact2.email = @"bbbb@bbbb.com";
    contact2.phoneNumber = [NSNumber numberWithInt:2222];
    
    Contact *contact3 = [NSEntityDescription
                         insertNewObjectForEntityForName:@"Contact"
                         inManagedObjectContext:context];
    
    contact3.name = @"cccc";
    contact3.email = @"cccc@cccc.com";
    contact3.phoneNumber = [NSNumber numberWithInt:3333];
    
    //4. Save attributes by saving context
    NSError *error;
    if (![context save:&error])
    {
        NSLog(@"Failed to save: %@", [error localizedDescription]);
    }
}

-(void)fetchContacts
{
    //1. Get entity by using name and context
    NSManagedObjectContext *context = [self managedObjectContext];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Contact"
                                              inManagedObjectContext:context];
    
    //2. Create fetch request by setting entity
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [fetchRequest setEntity:entity];
    
    //3. Fetch objects
    NSError *error;
    NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
    
    //4. Print fetched objects
    for (Contact *contact in fetchedObjects)
    {
        NSLog(@"Name: %@", contact.name);
        NSLog(@"Email: %@", contact.email);
        NSLog(@"Phone: %@", contact.phoneNumber);
        NSLog(@"----------------------");
    }
}



8. Call methods addContacts and then fetchContacts from viewDidLoad


    [self addContacts];
    [self fetchContacts];


9. RUN ( Delete and Run, otherwise same contact will appear twice on second run )



10. Check console for output


/*
 Expected output

 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Name: aaaa
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Email: aaaa@aaaa.com
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Phone: 1111
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] ----------------------
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Name: bbbb
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Email: bbbb@bbbb.com
 2015-06-03 02:01:41.443 CoreDataTutorial[2861:26823] Phone: 2222
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] ----------------------
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] Name: cccc
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] Email: cccc@cccc.com
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] Phone: 3333
 2015-06-03 02:01:41.444 CoreDataTutorial[2861:26823] ----------------------

 */



What should you learn next ?


As learning curve is huge, listing some pointers for important topics...

1. Relationship setup by using multiple tables (entities)
2. NSFetchResultController, Binding your data from database with some view ( showing subset of fields from DB on view ), any operation on database e.g update, add, remove etc reflects data on view automatically.
3. Using Model Editor Interface for writing queries
4. Light weight migration
5. Debugging Select the ‘Run’ scheme and select the ‘Arguments’ tab. Add the following argument: “-com.apple.CoreData.SQLDebug 1”.
6. Common errors and crash resolving
7. Working with multiple threads  
8. Faulting




No comments:

Post a Comment