Response: The Laws of Core Data

by Marcus Zarra

Recently, I saw a post from Dave DeLong and knowing his history with the framework I read it with extreme curiosity.

Unfortunately, that read led to this post as I disagree strongly with a number of the laws he has laid down.

Since I know people are going to be asking me about this post (I am still getting asked about Simmons’ post from a VERY long time ago), better to get my thoughts out now.

1. Do not use Core Data as if it were a database

No disagreement; Core Data is your object model and it can persist to a database but that is not its biggest strength.

2. Do not use Core Data as if it were a SQLite wrapper

This is re-iterating #1. Core Data is your object model, not a thin wrapper around a database.

3. Your NSManagedObjectContext is your “stack”

Here is where we start to disagree. The NSManagedObjectContext is not your stack. It is a scratchpad. It is where you stage changes to the data objects and decide to persist them or not.

I have advocated putting an object around it for convenience and Apple added the NSPersistentContainer which is designed to be subclassed. That wrapper is a great place to handle migrations, importing, exporting, convenience methods for accessing data; the list goes on.

All of the internal workings of your Core Data stack, your persistence and data model absolutely should be stored in the persistent container and that container should be injected through your controllers in your application.

4. Never ever ever ever ever use an NSManagedObject outside its context’s queue

No disagreement. Core Data is designed to work in a threaded environment but most of the objects are NOT designed to work on multiple threads/queues. As with ANY object, assume it is thread locked unless you can demonstrably confirm that it is safe to use across threads.

5. Do not use NSManagedObject as if it were an NSObject

An NSManagedObject is an NSObject that has additional functionality added. It should absolutely be treated as if it is an NSObject because it is an NSObject. NSManagedObject instances are your data objects and should be treated as such. Creating Plain Old Objects on top of NSManagedObject instances is asking for pain and data corruption.

6. You usually don’t need parent-child contexts

If you are not accessing the network, exporting data, importing data or interacting with the outside world in any way, shape or form then this is correct.

If your app talks to anything else then you will want to use parent-child contexts.

The parent context should be on the user interface thread (the main thread) and when you need to do asynchronous work with your data objects (importing or exporting) then you want to do that on another thread and that work should be done in a child context.

A child context simplifies the transfer of notifications of data changes and greatly simplifies using Core Data in a multi-threaded environment.

7. Keep your main queue context read-only

The main queue is also known as the user interface queue. The context that is associated with that queue should be for the user. That context reads and writes data when the user is interacting with the application.

Should it be read-only? Absolutely not.

Should it be user-only? Absolutely.

Any non-user work (importing and exporting) should be done on a child context on a background thread (see response to #6 above).

8. Use an abstraction layer

Please do not do this. You are going to make the next developer curse your name in a very unpleasant manner.

Use Core Data in your user interface. Do not create dummy data objects on top of Core Data. That leads to a maintenance nightmare.

Your user interface can use Core Data without really knowing that it is using Core Data. Your user interface treats the NSManagedObject instances as if they were data objects because they are data objects.

Your user interface does not need to know any more about Core Data than it does for any other model structure. Your user interface should talk to your persistent container subclass and ask for the data it needs. The response to that request might be instances of NSManagedObject or might be an NSFetchedResultsController which then serves up the data objects in a consistent and reliable way.

9. Use Core Data as a local cache

I agree with the wording of this rule but I suspect the meaning is different. Core Data, like any local persistence, is a local cache of what is on the server. This is not specific to Core Data because that is what any local persistence does when you have data on a server.

Core Data is more than just a local cache.

Core Data is not complicated

Core Data is as complicated as you make it. Most of the time you do not need or want the advanced features of Core Data and can just grab an instance of NSPersistentContainer and be happy.

When you need to deal with more complicated or interesting situations there is a fair chance that Core Data has an advanced feature to handle that situation. For the 80%, you can ignore those advanced features and relax in the comfort knowing they are there for if/when you need them.

The harder your code is to write the harder it is going to be to maintain and debug.

Don’t make it harder on yourself. Keep it simple.