Why you should use Core Data?

The answer

As often happens for many other similar questions, the answer is: “Is there a good reason you should not?”. This might seems to be reductive and not explanatory at all, but it summarizes a lot of the aspects we will discuss further in the article.

In particular we will discuss about what Core Data is, about what Core Data is not, its basic structure and its methods to store and retrieve data. Finally I will try to tell why, in my opinion, this awesome framework will hugely simplify your model storage approach, how it will save your time and preserve your karma

Before starting to talk about Core Data, let’s analyze what are the alternatives to this framework.

Property Lists

If you are not new to iOS/OSX programming, you have surely dealt with some file with a .plist extension. For example, the file YourProjectName-Info.plist contains information about your project configuration, such as the application name, the bundle identifier, the allowed orientation and so on. The property list are basically XML files handled by Xcode in a fancy way; they can store arrays, dictionaries, boolean, numbers, dates and data.

This is a very limitative approach, in fact one problem with this method is that custom objects cannot be serialized into property lists.

If you simply need to store an array of NSStrings, well this might be the best solution, you simply have to call the writeToFile:atomically: method on your array and you’re done! But I’m sure you are here because you need a more consistent approach to data persistence..

NSCoding + NSKeyedArchiver

This was one of the first approaches to data persistence available in iOS since its birth. It is based on object modeling and the adoption of a protocol: NSCoding.

By the implementation of

initWithCoder:(NSCoder *)decoder

and

encodeWithCoder:(NSCoder *)encoder

you can specify the key to use in order to store a specific property of an object and then retrieve it when you need. With an NSKeyedArchiver you can store objects conforming to the NSCoding protocol and fetch them using an NSKeyedUnarchiver instance.

Even if this is a very basic approach, you need to write a ton of code to save a simple object, it is not flexible and every time you have to change a property,  you must update the code that stores it too. This is not only error-prone, but it lacks of an important feature: complex relationships. Just think about a one-to-many relationship between two objects, this could be a very difficult feature to implement. Another feature you will surely need is to perform a query on your model. Even if you need a simple join between two group of objects, with NSCoding would be a pain in your back. NSCoding+NSKeyed(Un)Archiver is a good option if your model is really simple and *-to-many relationship free.

If you are interested in the NSCoding protocol and its application, I suggest this article on NSHipster.

SQLite3

Here we are, the good ‘ol SQL based dbms! SQLite is without of doubts the most powerful way to store your objects persistently, it provides all the features you need for this purpose: strong organization in tables, SQL language, powerful join features and nested queries and you can even creates indexes and triggers! As it often happens, if some framework gives you every possibility and the maximum flexibility, it’s likely to produce boiler-plate code. If you’re not persuaded by this sentence, let’s make a real-world example. Suppose we have a Message object containing an id,a sender, a receiver, a date and of course the message. This could be the code to create its Message Table:

    
    sqlite3 *database;
    if (sqlite3_open(DBPath, &database)
        != SQLITE_OK) {
        sqlite3_close(database);
        NSAssert(0, @"Failed to open database");
        return;
    }

    const char *createSQL = "CREATE TABLE IF NOT EXISTS MESSAGES"
    "(messageId INTEGER PRIMARY KEY,message TEXT,sender TEXT,receiver TEXT,date TEXT)";
    sqlite3_exec(database, createSQL, NULL, NULL, &errorMsg);

    if (errorMsg != NULL){
        sqlite3_close(database);
        NSAssert(0, @"Failed to create TE_PROJECTS table: %s",errorMsg);
        return NO;
    }

    sqlite3_close(database);

And this could be an example of a fetch operation:

       
    sqlite3 *database;
    if (sqlite3_open(DBPath, &database) != SQLITE_OK) {
        sqlite3_close(database);
        NSAssert(0, @"Failed to open database");
        return;
    }

    NSString *query = [NSString stringWithFormat:@"SELECT message FROM MESSAGES WHERE messageId = 4"];
    sqlite3_stmt *statement;

    NSString *savedMessage = nil;
    if (sqlite3_prepare_v2(database, [query UTF8String],-1, &statement, nil) == SQLITE_OK) {
        while (sqlite3_step(statement) == SQLITE_ROW) {
            char *message = (char *)sqlite3_column_text(statement, 0);
            savedMessage = @(message);             
        }             
        sqlite3_finalize(statement); 
    }         
    sqlite3_close(database);

Finally this could be an insertion:

            
    sqlite3 *database;
    if (sqlite3_open(DBPath, &database) != SQLITE_OK) {
        sqlite3_close(database);
        NSAssert(0, @"Failed to open database");
        return;
    }    char *update = "INSERT OR REPLACE INTO MESSAGES (messageId,message,sender,receiver,date) VALUES (?,?,?,?,?);";
    sqlite3_stmt *stmt;
    if (sqlite3_prepare_v2(database, update, -1, &stmt, nil) == SQLITE_OK) {
        sqlite3_bind_int(stmt, 1, 34);
        sqlite3_bind_text(stmt, 2, "some spare text", -1, NULL);
        sqlite3_bind_text(stmt, 3, "John", -1, NULL);
        sqlite3_bind_text(stmt, 4, "Frank", -1, NULL)
        sqlite3_bind_text(stmt, 5, "2013-03-21", -1, NULL);
    }
    if (sqlite3_step(stmt) != SQLITE_DONE){
        NSLog(@"Error inserting entry in table MESSAGES");
        return;
    }
    sqlite3_finalize(stmt);    sqlite3_close(database);

This huge amount of code is the bare essential to have your object stored in a database. Just think about how many times you have to write the same code to create a table, to save a simple string or to fetch an object from your database! When you are repeating too much code too much times, there is always a better way to achieve the same result, and most of all someone surely have already done the job for you! This sounds good isn’t it?  Let’s keep SQLite for our database intensive operations like creating a trigger or perform evil full outer join on tables and let’s move on the subject of this article, Core Data.

What Core Data is

Apple definition: “The Core Data framework provides generalized and automated solutions to common tasks associated with object life-cycle and object graph management, including persistence.”. This seems to be pretty simple and self-explanatory, Core Data is a framework that takes care of your data model, provides consistency mechanisms and deals with the file system/dbms to store data.

What Core Data is not

Since Apple tell us very well what Core Dat is not, I reported down here their definition:

• Core Data is not a relational database or a relational database management system (RDBMS).

Core Data provides an infrastructure for change management and for saving objects to and retrieving them from storage. It can use SQLite as one of its persistent store types. It is not, though, in and of itself a database. (To emphasize this point: you could for example use just an in-memory store in your application. You could use Core Data for change tracking and management, but never actually save any data in a file.)

• Core Data is not a silver bullet.

Core Data does not remove the need to write code. Although it is possible to create a sophisticated application solely using the Xcode data modeling tool and Interface Builder, for more real-world applications you will still have to write code.

At this point your ideas may still be confused, I haven’t provided a true answer to the main question, I have only focused on the bad aspects of the other approaches. This article is not meant to be a Core Data tutorial, nor a deep focus on the technology, so to get things more concrete, the only thing I will do is providing a short example of storing and fetching data.

How many code you need to write with Core Data?

Let’s take our previous example, the message Class:

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@interface Message : NSManagedObject

@property (nonatomic, retain) NSNumber * messageId;
@property (nonatomic, retain) NSString * sender;
@property (nonatomic, retain) NSString * receiver;
@property (nonatomic, retain) NSDate *date;
@property (nonatomic, retain) NSString *message;

@end

This is the interface of an auto-generated NSManagedObject subclass. Core Data creates automatically these objects for you, based on an Xcode Data Model Diagram. In this file you can graphically create your object s and the relationships between them, Core Data automatically crates the backend structure for you. Here is an example:

DataModelExample.jpg

An xcode data model diagram example.

Let’s see how to store an instance of a managedObject:

    NSManagedObjectContext *moc = [[UIApplication delegate]managedObjectContext];
    Message *message = (Message *)[NSEntityDescription insertNewObjectForEntityForName:@"Message"
                                                        inManagedObjectContext:moc];
    message.sender = @"SenderName";
    message.receiver = @"receiverName";
    message.message = @"Hello";
    message.date = [NSDate date];
    message.id = 123;

    if (![moc save:&error]) {
        // Save failed
	NSLog(@"Core Data Save Error: %@, %@", error, [error userInfo]);
    }

As you can see, at this point you haven’t written a single line of SQL, you didn’t care about data formatting and file locations, Core Data did everything for you.
Let’s go ahead and see how to fetch a managed object from it’s context:

NSManagedObjectContext *moc = [[UIApplication delegate]managedObjectContext];

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Message" inManagedObjectContext:moc]];

// Add a sort descriptor. Mandatory.
NSSortDescriptor *desc = [NSSortDescriptor sortDescriptorWithKey:@"sender" ascending:YES];
[fetchRequest setSortDescriptors:@[desc]];
fetchRequest.predicate = [NSPredicate predicateWithValue:YES];NSError *error;NSArray *fetchResults = [moc executeFetchRequest:fetchRequest error:&error];if (fetchResults == nil) {
    // Handle the error.
    NSLog(@"executeFetchRequest failed with error: %@", [error localizedDescription]);
}

As you can see again, no SQL needed, no database instantiation, no C structures to handle. All you have to do is to learn how to use NSPredicate and NSSortDescriptor which are really,really easy to use. This seems to be anyway a lot of code to write every time, luckily there is a very good solution provided by  Chris Miles which you can found it here.  What he does is simply create a set of generic functions to store, fetch and delete managed objects and perform a rollback in a managedObjectContext. If you decide to use Core Data, his approach is possibly the most clean and powerful to adopt.

For example with his approach, storing an object would be this easy:

[Message insertMessageWithSender:@"SenderName" receiver:@"ReceiverName" date:[NSDate date] id:@(123)];
commitDefaultMOC();

The top most feature of Core Data is that the related objects to an object instance are loaded for free. For example, let’s say we have two Classes:  Person and Car. A Person can own more cars, a car can be owned by  only one person. Now insert a person and 3 cars in the context, assign to every car the person as the owner and perform save on the context. If you try to load the person you just stored and look at his cars property, you will see that all the 3 car instances are already loaded with their owner object! Just think about the effort you would have done to store and fetch a many to many relationship with SQLite…

Conclusions

In this article we have covered a lot of aspects related to Core Data and its alternatives. Every approach has its pros and cons, and every one should be evaluated when we design the data persistence strategy. Core Data is indeed your first choice if the complexity of the model structure becomes a pain to manage.

Advertisements

2 thoughts on “Why you should use Core Data?

  1. Pingback: Why you should use Core Data? : DM Digital Blog

  2. Pingback: iPhone Interview Questions | iBlogs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s