Core Data provides a lot of tools and capabilities to handle almost any type of data. The downside is that has a lengthy learning curve.
Note: In the accompanying sample application, all Core Data code is located in the source files GB_CarsCoreData.m and .h.
SQLite is an open source, lightweight, robust, well-supported, self-contained, cross-platform, relational database that is very popular. You have probably used SQLite at some point in the past — it is a very good option for storing application data. The SQLite website is worth taking a moment to browse. Unlike many open source projects, SQLite documentation and support is excellent. SQLite version 3.7.13 is supported on iOS 6 and 7. SQLite is also supported on Android and Windows phone, which is a huge benefit. If you are developing a mobile application targeting multiple platforms, you'd be crazy not to use SQLite.
SQLite stores data in tables, where a table contains one or more columns, each column contains data for a specific data type. One of the tasks RDMS are good at is handling a normalized data set. Instead of each table containing all of the possible columns of data, resulting in duplicated data for each table row, the tables are typically organized to reference another table (via a key), that contains the duplicated data. In the sample application, the manufacture information is kept in a separate mfg_info table which is referenced by the car table.
Your application interacts with SQLite via the SQL language and the SQLite C API (see sqlite.h). This means that to insert data into the database an insert SQL statement must be created along with the data to insert. The following snippet shows the insert statement used by the sample application.
cardetails_id) values ("Pinto", 17000, 1970, 1, 2, 3)"
"select car.model, car.year, car.msrp, cardetails.detailgroup, "
"cartype.type_desc from car inner join cartype on "
"car.cartype_id = cartype.id inner join cardetails on "
"car.cardetails_id = cardetails.id inner join manufacturer on "
"car.mfg_id = manufacturer.id where car.id = %@"
As you can see, even a moderately involved data set requires a solid understanding of SQL syntax and behavior. Another drawback to SQLite is the tedious nature of ensuring that the columns and data are in the correct order. For example, when iterating through the SELECT results, if you make a mistake for the column number in the sqlite3_column_*() call, the wrong data will be fetched. This can easily happen when additional columns are added or removed later.
SQLite implements a C API; thus, you will have to convert between NSStrings * and char * strings when your application interacts with the database. There are Objective C wrappers for SQLite — the most popular being FMDB. For most applications, using the SQLite API directly is not overly cumbersome. The attached sample application uses the SQLite API directly.
Core Data and SQLIte are fundamentally different, so it is difficult to compare the two technologies directly. However, from a developer's viewpoint, you can gauge how these differences manifest themselves in an application. What is the cost and benefit of each approach? The accompanying sample iOS iPhone application is designed to test both technologies. From the main screen, select either Core Data or SQLite, create the desired number of records, and select the Car tab to fetch the records. The memory usage number is for the entire application and is updated once per second. The storage size is the size of the actual persistent file.
Using this test application, let's look at a few key metrics. An iPhone 4s and 5s were tested, both running iOS version 7.1.1. The type of iPhone used, 4s or 5s, for a result is show within parentheses. For exampe (4s) denotes test results for an iPhone 4s, (5s) is for an iPhone 5s.
Core Data uses more memory, from 40% to 100% more than SQLite. This makes sense considering how Core Data is designed (specifically, how the NSManagedObjectContext tracks all of the objects), where each object has a memory footprint of some size depending on whether the object's contents have been faulted in (realized in memory versus being a fault).
Table 3: Numbers after application start up and fetch of all Car records.
There is something to note about memory testing. When creating records, the entire Car record is created in memory and the higher memory usage reflects this. However, most user scenarios do not involve creating a large number of records. A better measurement of the memory usage is at application start up and with a fetch of all of the Car records. This is what Table 3 reflects. To get this measurement in testing, the application was restarted (terminated versus put in the background) and the records were re-fetched.
Core Data uses more storage space — a lot more storage space — approximately 4x more than SQLite, see Table 4.
Both Core Data and SQLite are fast when fetching records. For the iPhone 5s, the differences are slight. However, for the 4s, Core Data is nearly 2x as fast. Table 5 shows just one operation, fetching all of the Car records.
SQLite is, as advertised, lightweight.
SQLite uses less memory and storage space.
SQLite can be tedious and error-prone to code.
SQLite is supported on Android and Microsoft Windows Phone.
Longer learning curve: it takes some study to understand.
Objects are easier to work with.
Underlying storage details are handled atomically (support for iCloud).
No one technology, framework, or diet pill will make your life instantly better. When designing an application, a lot of criteria must be considered. This article provides some guidance when selecting a storage approach for your next iOS application. I encourage you to download the sample application and take it for a spin.
Dean Gereaux is a consultant engineer and principal of Golden Bits Software.