Core Data is a great choice for persisting and querying data in your iOS and OSX apps. Not only can it reduce memory usage and improve performance, but it can also save you from writing a lot of unnecessary boilerplate code.
In addition, the Core Data API is extremely flexible, which allows it to be used in a myriad of apps, all with different data storage requirements.
However, this flexibility means that sometimes Core Data can be slightly difficult to work with. Even if you’re a Core Data guru, there’s still a lot of mundane tasks required, and a lot of room to make silly errors.
Luckily, there are a lot of great tools that can help you out and make Core Data significantly easier to work with. Here are our top 10 picks that you should know and love!
Also note this article has an Objective-C focus since most Core Data libraries are written in Objective-C at the moment. If you want to learn how to use Core Data with Swift, check out our upcoming book Core Data by Tutorials, which is fully updated for iOS 8 and Swift!
10. RestKit
RestKit is an Objective-C framework for interacting with RESTful web services. It provides a Core Data entity mapping engine that maps serialized response objects directly to managed objects.
The code example below shows you how to set up RestKit to access the OpenWeatherMap API and map the JSON response of the /weather
endpoint into a WFWeather
managed object:
|
Here’s what’s going on in the code above:
- First, you create a
RKEntityMapping
object that tells RestKit how to map API responses to attributes ofWFWeather
. - Here,
RKResponseDescriptor
ties responses from/data/2.5/weather
to theRKEntityMapping
instance above. RKManagedObjectRequestOperation
defines which operation to execute. In this example, you request the weather in Orlando from the OpenWeatherMap API and point the response to the instance ofRKResponseDescriptor
noted above.- Finally, you execute the operation with the requisite success and failure blocks. When RestKit sees a response coming back that matches the defined
RKResponseDescriptor
it will map the data directly into your instance ofWFWeather
.
In the code above there’s no need for manual JSON parsing, checking for [NSNull null]
, manual creation of Core Data entities, or any of the other routine things that one must do when connecting to an API. RestKit turns API responses into Core Data model objects via a simple mapping dictionary. It doesn’t get much easier than that.
To learn how to install and use RestKit, check out our Introduction to RestKit tutorial.
9. MMRecord
MMRecord is a block-based integration library that uses the Core Data model configuration to automatically create and populate complete object graphs from API responses. It makes generating native objects from web service requests as simple as possible as it creates, fetches, and populates NSManagedObject
s instances for you in the background.
The code block below shows how to use MMRecord to perform the same Orlando weather call and data mapping that you did in the above in the RestKit example:
|
Without writing any complicated networking code or manually parsing the JSON response, you’ve called an API and populated your Core Data managed objects with the response data in only a few lines of code.
How does MMRecord know how to locate your objects in the API response? Your managed objects must be subclasses of MMRecord
and override keyPathForResponseObject
as shown below:
|
keyPathForResponseObject
returns a key path that specifies the location of this object relative to the root of the response object from the API. In this case the key path is main
for the data/2.5/weather
call.
It’s not all magic — MMRecord does require that you create a server class that knows how to make requests against the API you’re integrating against. Thankfully, MMRecord comes with sample AFNetworking
-based server classes.
For complete information on setting up and using MMRecord, the readme on the MMRecord Github repository is the best place to start.
8. Magical Record
Modeled after Ruby on Rails’ ActiveRecord system, MagicalRecord provides a set of classes and categories that enable one-line entity fetch, insertion and deletion operations.
Here’s a view of MagicalRecord in operations:
|
MagicalRecord makes it easy to set up your Core Data stack. Instead of having many lines of boilerplate code, you can set up a full Core Data stack with only one method call in your AppDelegate file as follows:
|
You call setupCoreDataStackWithStoreNamed
in application:didFinishLaunchingWithOptions:
with the name of your SQLite file. This sets up your instances of NSPersistentStoreCoordinator
,NSManagedObjectModel
and NSManagedObjectContext
so that you’re all ready to work with Core Data.
Have a look at our MagicalRecord tutorial for further information on how to install and use MagicalRecord.
7. GDCoreDataConcurrencyDebugging
Concurrency issues are some of the hardest things to debug in Core Data. The performBlock
APIs help, but it’s still easy to make mistakes.
The open source project GDCoreDataConcurrencyDebugging
can be added to your own projects to alert you via console messages when NSManagedObject
s are accessed on the wrong thread or dispatch queue.
Below is an example of accessing an instance of NSManagedObject
from the wrong context:
|
In the code above you’re trying to read name
in context2
from an object that was originally created in context1
.
If you were to run the above example using GDCoreDataConcurrencyDebugging
you’d see the following console message that advises you of the problem:
2014-06-17 13:20:24.530 SampleApp[24222:60b] CoreData concurrency failure |
GDCoreDataConcurrencyDebugging
from your app before you ship a build to the App Store as it does add a small amount of overhead that doesn’t need to be in your published app.
Core Data under iOS 8 and OS X Yosemite now has the ability to detect concurrency issues. To enable this new functionality you passing -com.apple.CoreData.ConcurrencyDebug 1
to your app on launch via Xcodeʼs Scheme Editor.
However, until you can phase out support for earlier OS versions in your app, GDCoreDataConcurrencyDebugging
will keep you advised of concurrency issues during development.
The GDCoreDataConcurrencyDebugging README on Github is your best resource information on installing and using this tool.
6. CoreData-hs
CoreData-hs generates category methods to execute common fetch requests for all entities and properties in your Core Data Model. Creating these methods isn’t difficult, but it is time consuming — and every little bit of time saved coding is valuable!
For example, if your weather app had a view with the weather forecast and modeled each day’s forecast using a WFForecast
entity with a timeStamp
, temp
, and summary
attribute, CoreData-hs would create the following category for you:
|
As you can see there are a lot of methods generated! As an example, here’s the implementation generated for tempIsGreaterThan:inContext:sortDescriptors: error:
:
|
Once the methods have been generated you can now use them to perform fetch requests with specific conditions. For example, if you need to fetch all WFForecast
objects where the temperature is over 70° you can call tempIsGreaterThan:inContext:sortDescriptors:error:
and simply pass in the target temperature as shown below:
|
You’ll get back an array of matching objects.
CoreData-hs is a lightweight utility that can save you time if you tend to generate a lot of these types of request by hand. For installation and usage instructions, consult the README on Github.
5. Core Data Editor
You can view and edit your app’s Core Data-based models from inside the GUI of Core Data Editor, which supports XML, binary and SQLite persistent store types. Beyond editing basic attributes, you can also edit and visualize data relationships. You can also use Mogenerator (discussed in item #2 below) with Core Data Editor to create your model code.
Core Data Editor is familiar with Apple’s schema and presents your data without the Z prefixes you might be familiar with if you’ve ever looked at the SQL files that Core Data generates. You can browse the contents of your app’s database in a nice table format. It also supports previewing of binary data such as pictures, and in-line editing of dates using a standard date picker:
If you need to create a seed file or just want to import data, Core Data Editor can take in a CSV file and turn it into persisted objects in Core Data as shown below:
To install Core Data Editor, download the free trial from the Thermal Core website. Uncompress the downloaded ZIP archive and move the Core Data Editor.app file to yourApplications directory. The author of the app has also recently open sourced it if you want to find out how it works and make your own enhancements.
When you launch the app for the first time it will guide you through a short setup process. This process is optional but it will speed things up later if you specify, at a minimum, your iPhone Simulator directory and your Xcode derived data directory.
Note: Because you’re required to select your derived data and simulator directories in the GUI, you may run into trouble with default settings in OS X Lion and up that hide your Libraries folder.
In Mavericks OS X, you can correct this by going to your home directory in the finder and selecting View / Show View Options and checking Show Library Folder. In Lion and Mountain Lion OS X, the same thing may be accomplished by typing chflags nohidden ~/Library/
into Terminal.
More details about Core Data Editor can be found on Thermal Core’s website.
4. SQLite3Sometimes performing SQL queries directly on the underlying Core Data SQLite database can be helpful when debugging a knotty data issue. SQLite3 is a Terminal-based front-end to the SQLite library that comes installed on all Macs and should be familiar to those with extended database experience. If you don’t have extended database experience, this probably isn’t for you.
To use SQLite3, first open Terminal and navigate to your app’s Documents directory. Depending on your install, the Documents directory will be similar to ~/Library/Application Support/iPhone Simulator/7.1-64/Applications/{your app's ID}/Documents
.
Change 7.1-64 from the above command to match the version of the simulator you’re using. {your app’s ID} is automatically generated by Xcode and uniquely identifies each app installation. There’s no easy way to find out which ID is yours. You can either add logging to your app when you create the core data stack, or look for the directory that was modified most recently – this will be the app you’re currently working on :]
The documents directory will contain a file with the extension sqlite
, which is your app’s database file. For apps using Apple’s core data template, the filename will match your app’s name. Open this file using the SQLite3 program as follows (the example app here is called AddressBook, your filename will be different):
$ sqlite3 AddressBook.sqlite |
You’ll see the following prompt appear in the console:
SQLite version 3.7.13 2012-07-17 17:46:21 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> |
Now you’re ready to perform standard SQL queries against the database.
For example, to view the schema Core Data is using, execute the following command:
sqlite> select * from sqlite_master; |
SQLite responds to your query with a textual listing of the tables in the schema as follows:
table|ZMDMPERSON|ZMDMPERSON|3|CREATE TABLE ZMDMPERSON ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZISNEW INTEGER, ZFIRSTNAME VARCHAR ) table|Z_PRIMARYKEY|Z_PRIMARYKEY|4|CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER) table|Z_METADATA|Z_METADATA|5|CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB) sqlite> |
The Z prefixes on all of the table columns are part of Core Data’s underlying use of SQLite. For analysis purposes, it’s safe to ignore them.
Note: You should never write to the SQLite Core Data database directly. Apple can modify the underlying structure at any time.
If you truly have a need to directly manipulate the SQLite database in a production application, you should forgo Core Data and use raw SQL access instead. There are several popular frameworks to help you manage SQL implementation in your apps, including FMDB and FCModel.
If you’re just analyzing your data, there’s nothing wrong with poking around the SQLite database file — just don’t modify its contents.
One example of using direct SQL to analyze your data is grouping and counting distinct attributes to see the diversity of your attributes.
For example, if you have a sample address book app and want to know how many of your contacts live in each city, you could execute the following command at the SQLite3 prompt:
SELECT t0.ZCITY, COUNT( t0.ZCITY ) FROM ZMDMPERSON t0 GROUP BY t0.ZCITY |
SQLite would respond with the count of each distinct city in your address book database, as shown in the example below:
San Diego|23 Orlando|34 Houston|21 |
To exit the SQLite3 terminal program, simply execute the following command:
sqlite> .exit |
For more information on SQLite3, view its man
page by opening Terminal and executing the command man sqlite3
.
3. MDMCoreData
MDMCoreData (disclaimer – this library is written by me!) is a collection of open source classes that make working with Core Data easier. It doesn’t try to hide or abstract Core Data, but instead enforces best practices and reduces the amount of boilerplate code required. It’s a better alternative than the Xcode Core Data Template.
MDMCoreData consists of the following four classes:
- MDMPersistenceController – A handy controller that sets up an efficient Core Data stack with support for creating multiple child-managed object contexts. It has a built-in private managed object context that saves asynchronously to a SQLite store.
- MDMFetchedResultsTableDataSource – Implements the fetched results controller delegate and a table data source.
- MDMFetchedResultsCollectionDataSource – Implements the fetched results controller delegate and a collection data source.
- NSManagedObject+MDMCoreDataAdditions – A category on managed objects providing helper methods for eliminating boilerplate code such as entity names.
One great feature of MDMCoreData is that it comes with a Core Data backed table data source — so you don’t have to worry about implementing one yourself.
Instead of implementing all the required methods in the UITableViewDataSource
and NSFetchedResultsControllerDelegate
protocol, you can just set your table’s data source to an instance of MDMFetchedResultsTableDataSource
. When instantiating the MDMFetchedResultsTableDataSource
object you simply pass in the table view and a fetched results controller:
|
A MDMFetchedResultsTableDataSource
does have a delegate, with two methods that must be implemented. One method configures the cell for your table:
|
The second method handles deletions:
|
It’s far easier to implement the two required methods of MDMFetchedResultsTableDataSource
than to implement all of the methods required by the table data source and fetch results controller protocols.
You can find out more about MDMCoreData at the MDMCoreData Github repository.
2. Mogenerator
Since Core Data comes with full support for key-value coding (KVC) and key-value observing (KVO), there is no requirement to implement custom NSManagedObject
classes. You can get by using setValue:forKey:
and valueForKey:
when reading or writing attributes on your entities. But this tends to be cumbersome and hard to debug since strings can’t be checked at compile time for correctness.
For example, if you had a person
Core Data entity, you could read and write attributes like this:
|
The person
object above is an instance of NSManagedObject
with an attribute named firstName
. To read firstName
, you use valueForKey:
with the key firstName
. Similarly, to set the first name of a person
object you can use setValue:forKey:
.
A better approach is to use standard accessor methods or dot syntax; however, to do this you must implement a custom subclass of NSManagedObject
for your entities. This lets you add model logic such as fetch requests and validation.
You have probably used Xcode’s Create NSManagedObjectSubclass functionality to quickly create a subclass for a single entity. Although it’s a nice shortcut, it can create extra overhead if you have a large model and can cause you grief when your model changes.
Re-creating the subclass means wiping out all of your custom model logic — which means you should host that logic outside of your custom model. This lends itself to a common pattern of creating custom subclasses with managed object properties along with categories for custom model logic.
The command line tool Mogenerator automates these exact tasks for you. It generates two classes per Core Data entity. The first class is for machine consumption and is continuously overwritten as the model changes. The second class is for all your custom logic and is never overwritten.
Mogenerator has a list of other benefits which include the following:
- No need to use
NSNumber
objects when reading or writing numeric attributes. - Helper methods for working with sets.
- Helper methods for creating new entities
- A method for entity identification.
Mogenerator can be installed from the DMG available on the Mogenerator website, or alternatively through Homebrew. To install Mogenerator using Homebrew, open Terminal and run the following command:
brew install mogenerator |
Once installed, use the cd
command to change to your app’s directory, then run Mogenerator from Terminal like so:
$ mogenerator -m MySampleApp/ExampleModel.xcdatamodeld -O MySampleApp/Model --template-var arc=true |
In the command above, you call Mogenerator followed by the location of your model with the -m
option. You can also specify where the generated classes should be located with the -O
option. When working with ARC you should also pass the --template-var arc=true
option.
You can make Xcode run Mogenerator for you by creating a Run Script Build Phase. Build Phases are descriptions of tasks that need to be performed by Xcode during a build.
To add a Build Phase, first select the target, select the Build Phases tab, then select Editor / Add Build Phase / Add Run Script Build Phase from the menu.
Add the following code in the Shell script text area under the new Run Script, making sure to modify the parameters to mogenerator
as suits your project:
if [ "${CONFIGURATION}" == "Debug" ]; then echo "Running Mogenerator" mogenerator -m MySampleApp/ExampleModel.xcdatamodeld -O MySampleApp/Model --template-var arc=true echo "Finished Mogenerator" else echo "Skipping Mogenerator" fi |
The above run script will cause Xcode to run Mogenerator every time you run a debug build command. If there are no changes to the model, Mogenerator will do nothing and exit.
Now that you have incorporated Mogenerator into your workflow for quick subclass generation, you should take advantage of its other features.
For example, instead of unwrapping primitive values every time you can just add the suffix Value
on to them as illustrated by the following code snippet:
|
Since bool types are stored as NSNumber
in Core Data, you must call boolValue
on the person
object before checking if the value is true. With Mogenerator, that extra step is no longer required as you can simply call isFriendValue
.
If Mogenerator looks like a useful addition to your toolbox, you can find more information on Mogenerator at its Github repository.
1. Instruments
Instruments is the tool of choice for investigating almost all performance and memory issues on OS X and iOS — including Core Data issues. The other tools in this list offer a lot of automation and convenience, but Instruments will typically be your first stop when investigating any issues or doing any performance tuning.
The Time Profiler and Core Data templates, shown below, are the most useful for Core Data profiling:
The default Core Data template, with the optional Faults Instrument feature added in, provides the following features to help you tune and monitor your app’s performance:
- Core Data Fetches Instrument — Captures fetch count and duration of fetch operations.
- Core Data Cache Misses Instrument — Captures information about fault events that result in cache misses.
- Core Data Saves Instrument — Captures information on managed object context save events.
- Core Data Faults Instrument — Captures information on fault events that occur during lazy initialization of
NSManagedObject
s or relationships.
Here is a typical instruments profile from a Core Data app. You can see when fetch requests are occurring and how long they take, when and how often save operations happen and whenever faults are being fired:
For more information about Instruments, check out our tutorial on How to Use Instruments in Xcode.
Where To Go From Here?
Core Data is a powerful framework, but it comes with a lot of development overhead. However, the tools and libraries in this article give you some methods to help you efficiently and effectively tackle that overhead.
Again, if you want to learn about how to use Core Data with Swift, check out our upcoming book Core Data by Tutorials, which is fully updated for iOS 8 and Swift.
In the meantime, if you have any other Core Data tools or libraries you really would like to recommend to others, please join the forum discussion below!