How To Preserve User Data Using SQLite or Core Data on Apple Devices

Share this article

When building an iOS application, one thing to consider is a concept called “data persistence.” Data persistence is a method of holding onto data in case the application or device is reset. No one wants to be playing a game or updating their checkbook and lose everything after a reset because a poorly-designed app did not store their information. There are many ways to ensure that user data is captured and preserved, but the two methods that I want to highlight are using SQLite databases and using Apple’s Core Data framework. Apple has embedded functionality to use SQLite databases within your application. This method is exclusively for SQLite databases, and not mySQL databases. Whats the difference? The main difference between the two is that SQLite databases are embedded in the application, and they’re one standalone file. MySQL databases take up much more space due to their dependence on multiple files, and they cannot be embedded within an application. You’ll need a special editor to view these SQLite databases. I use and recommend SQLVue Lite. There is also a full version that costs more and offers a few extra features. Apple’s Core Data framework, unlike SQLite, takes more of an object-oriented approach to storing user data. With Core Data, you create entities that resemble classes and objects to store data. These entities are held in a data model, which is used to store various properties for the data. You can store attributes that resemble instance variables in Objective-C, relationships between objects and other data, as well as fetched properties, which are very similar to the aforementioned relationships. The only difference between relationships and fetched properties is that relationships pull all data preemptively with the object, while fetched properties recognize the existence of related data, but do not pull it right away. Before we define the exact differences between each method, let’s check out some example code that would be used for each technique. Comparing the two code samples will give us a better idea of the differences between the two methods.

SQLite

As you might imagine, this method requires the use of SQL statements. If you are not familiar with those, it may help to take a look at how these are structured before getting too deep. Some good documentation can be found here. (Note: All examples presented are from XCode 4.2) Before we get started, we need to include the dynamic library that will provide all of the necessary functions for our data storage. Locate your iOS SDK on your system and go to usr/lib/libsqlite3.dylib. To access a database, we first have to open the connection to it within the code. This part is surprisingly easy. The code simply looks like this:
sqlite3 *database;

int result = sqlite3_open(/path/to/database/file”, &database);
Then, when you are done with it, you close the statement.
sqlite3_close(database);
There is no reason to open and close a database without making any meaningful changes. The whole point of this is to make use of the database, so lets take a look at the sqlite3_exec command. There is a bit of a trick to this. You would think that you’d use this command in all cases, but that is not true. This is only viable for SQL statements that do not return any data. This would include DQL commands like CREATE, INSERT, UPDATE, DELETE, etc. Lets take a look at two different methods to retrieve data using a SELECT statement. The first method is fairly basic, but it is a little more complicated than the equivalent statements would be if passed through sqlite3_exec.
NSString *query = @”SELECT ID, FIELD_DATA FROM FIELDS ORDER BY ROW”;

sqlite3_stmt = *statement;

int result = (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil);

while(sqlite3_step(statement) == SQLITE_ROW){

int rowNum = sqlite3_column_int(statement, 0);

char *rowData = (char *)sqlite3_column_text(statement, 1);

NSString *fieldValue = [[NSString alloc] initWithUTF8String:rowData];

// do something with the data

[fieldValue release];

}

sqlite3_finalize(statement);
There are a few important steps in the above markup. First, we create our SQL statement. Then, we have to prepare the database to be utilized. When looking at the sqlite3_prepare_v2 function, we see that the first parameter is the database variable that we created. Then, we take our query string and turn it into a UTF8 based C string. Finally, we have our return statement. We have the possibility to store any unused portions of the query, but we set it to nil. Once we have those statements in place, we start cycling through all of the results using the sqlite3_step(statement) and making sure each becomes a row within the database. The rest is fairly basic. We first figure out which row we are on and what data it holds, and we store it in an NSString object that we can then use.  This is the part where you can get creative with the data. A very common tactic is storing the data as an NSDictionary or NSArray to be used when displaying lots of information, or when using a table view. The other method I mentioned is conceptually the same, but we can use variables in the statement to grab specific information. There is a slight difference, and for more practice, let’s use an INSERT statement and therefore the sqlite3_exec command.
char *sql = “insert into foo values (?,?);;

sqlite3_stmt *stmt;

if(sqlite3_prepare_v2(database, sql, -1, &stmt, nil) == SQLITE_OK){

sqlite3_bind_int(stmt, 1, 235);

sqlite3_bind_text(stmt, 2, ”bar”, -1, NULL);

}

if(sqlite3_step(stmt) != SQLITE_DONE){

NSLog(@”This should be real error checking!);

}

sqlite3_finalize(stmt);
The processing methods between the two techniques differ in several ways. Once again, we start by creating the query and the statement handle. Note that in the query string there are two question marks. These will end up being the values after we bind them. To bind them, we use the sqlite3_bind commands, adding one integer and one text value. The first parameter is the statement handle, then where we want to put it (first question mark is 1, second is 2) followed by the value. The rest of the SQLite statements should look very familiar, so we won’t restate the process. As a whole, that should give you a good idea of how to use SQLite in your applications. Now it is time to look at Core Data. To start with Core Data you need to create a data model which can be created by clicking “File” -> “New” -> “New File.” Then, under iOS on the left, select “Core Data” and then “Data Model.” Within this new file, you create your entities using the button on the bottom left of the view. Go ahead and create one. I just left mine as “Entity.” Now that we have the beginnings of Core Data, we can start digging into the code. Start by importing the framework CoreData from wherever you have your iOS SDK on your machine, and add <CoreData/CoreData.h> the AppDelegate.h within your application. Also, import AppDelegate.h into your ViewController.m. Then in ViewController.m make the following change to the ViewDidLoad function:
- (void)viewDidLoad

{

AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];

NSManagedObjectContext *context = [appDelegate managedObjectContext];

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@&quot;Entity&quot; inManagedObjectContext:context];

NSFetchRequest *request = [[NSFetchRequest alloc] init];

[request setEntity:entityDescription];

NSError *error;

NSArray *objects = [context executeFetchRequest:request error:&amp;error];

if(objects == nil){

NSLog(@&quot;There was an error!&quot;);

}

for(NSManagedObject *oneObject in objects){

NSNumber *lineNum = [oneObject valueForKey:@&quot;lineNum&quot;];

NSString *lineText = [oneObject valueForKey:@&quot;lineText&quot;];

NSString *fieldName = [NSString stringWithFormat:@&quot;line%d&quot;, [lineNum integerValue]];

UITextField *theField = [self valueForKey:fieldName];

theField.text = lineText;

}

[request release];

}
This method above does something that you don’t see very often in any application. Within the view controller, we are referencing the instance of the application delegate to get some base functionality for core data. The next few lines need a bit more explanation.
NSManagedObjectContext *context = [appDelegate managedObjectContext];

NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@&quot;Entity&quot; inManagedObjectContext:context];

NSFetchRequest *request = [[NSFetchRequest alloc] init];

[request setEntity:entityDescription];
Core data works within a context. The context is used to reference where all of the entities are stored. Then, we grab a description of our entity (named “Entity”) within that context we defined. Finally, we simply request all of the attributes, relationships, and fetched properties of that specific entity. Just like with SQLite3, we then cycle through all of the values in the object and assign them to variables to be used later. There is obviously a lot more that can be done with this technology, but these serve as basic examples to gain an understanding of the differences between the two methods. Before running this code, make sure that you add the following to AppDelegate.h.
NSManagedObjectContext *managedContext_;

NSManageObjectModel *managedObjectModel_;

NSPersistentStoreCoordinator *persistentStoreCoordinator_;
Also, be sure to add properties for these and synthesize them in the implementation file. So, what are the key difference between the two? The SQLite method was fairly complicated with the syntax, and it needed all of the string values to be C-based with the UTF8 format. On advantage to SQLite is that files can be easily viewed to see the relationships between different data. Conversely, the Core Data method is based on Objective-C, and stores data as manageable objects. This allows for a friendly interface to create objects and add properties. There is some more code involved, and using Core Data could potentially result in a larger file size compared to smaller SQLite databases, but it makes it very easy to fetch and determine what code needs to run in order to retrieve the data. Is one better than the other? Not necessarily. I personally prefer to use Core Data, since it does all of the dirty work for you. But, if you need a lightweight, streamlined app, or if you’re working with a very small amount of storage, SQLite may suit your purposes better.

Frequently Asked Questions about SQLite vs Core Data on Apple Devices

What are the main differences between SQLite and Core Data?

SQLite and Core Data are both persistent storage solutions used in iOS development, but they serve different purposes. SQLite is a relational database management system that allows you to perform complex queries and manage relationships between tables. On the other hand, Core Data is an object graph and persistence framework provided by Apple. It allows developers to manage and manipulate objects and their relationships, and also provides automatic data persistence.

Is Core Data faster than SQLite?

Core Data can be faster than SQLite in certain scenarios because it operates in memory, which is faster than disk operations. However, the performance difference is often negligible for small datasets. The choice between Core Data and SQLite should be based on your specific needs and the complexity of your data model, rather than performance alone.

Can I use both SQLite and Core Data in the same application?

Yes, it’s possible to use both SQLite and Core Data in the same application. However, it’s important to note that Core Data is not just a wrapper for SQLite. It provides additional features such as object graph management, change tracking, and undo support. If you need these features, it might be more efficient to use Core Data alone.

Is Core Data suitable for complex data models?

Core Data is highly suitable for complex data models. It provides an object-oriented interface to your data, which makes it easier to manage complex relationships and dependencies between objects. Additionally, Core Data’s ability to track changes and manage undo operations can be very useful in complex data scenarios.

How does data synchronization work in Core Data and SQLite?

Core Data provides built-in support for iCloud synchronization, which can be a big advantage if you’re developing an application that needs to sync data across multiple devices. SQLite does not provide built-in synchronization support, so you would need to implement this functionality yourself.

What are the advantages of using SQLite over Core Data?

SQLite has a few advantages over Core Data. It’s a standalone database engine that can be used with any programming language, not just Objective-C or Swift. It also supports complex SQL queries, which can be a big advantage if you need to perform complex data analysis or manipulation.

Is it difficult to migrate from SQLite to Core Data?

Migrating from SQLite to Core Data can be a complex process, especially if you have a large and complex data model. However, Apple provides a number of tools and resources to help with this process, including the Core Data Model Editor and the NSPersistentContainer class.

Can I use Core Data with a non-Apple database?

Core Data is designed to work with Apple’s SQLite database, but it’s also possible to use it with other database systems. However, this requires a significant amount of additional work, as you would need to write a custom persistent store to interface between Core Data and the non-Apple database.

How does Core Data handle concurrency?

Core Data provides several mechanisms for handling concurrency, including private and main queue types for managed object contexts. This allows you to perform data operations on separate threads without risking data corruption or inconsistencies.

What are the limitations of Core Data and SQLite?

Both Core Data and SQLite have their limitations. Core Data can be complex to set up and use, especially for beginners. It also requires a significant amount of boilerplate code. SQLite, on the other hand, does not provide built-in support for data synchronization or object graph management. It also lacks some of the advanced features of other database systems, such as stored procedures and triggers.

Tyler HermanTyler Herman
View Author
appleiosios tutorials
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week