If you’re an iOS developer you probably use Core data every day. I have been learning Core data for the last year professionally, and in my spare time for use in my own apps. I thought I would post some tips that maybe helpful in getting your head around this useful but difficult Apple framework.
Entities and Context
NSManagedObjectContext is one of the most important classes to understand. Apple calls this the scratch pad, another way to think of it is the reference to the your entities world
The general definition of a World is:
- a group of living things.
- a period of history.
- all of the people, societies, and institutions on the earth.
When first learning Core Data the context can be annoying, why cant I can just make changes to objects and make them stick? But if you think if it in terms of the world in which things happen, it makes more sense.
Every data change event that happens must be tied to a context, and context’s can never be traversed (although they can be merged). The
NSManagedObjectContext is your world according to core data and you should structure your code to always have access to it and make changes in reference to it.
Continuing on with the analogy, a living thing is your
NSManagedObject. In the context of its world, changes can happen to the living objects, they can be created and deleted. The world itself can even be deleted, which is handy for discarding edits.
Introducing threading can dramatically change the structure of your application without a safetey net, so extra caution should be taken. Debugging often becomes more difficult also. In core data the
NSManagedObjectContext can be tied to the main thread, or any thread during creation. If tied to the main thread your operations have the potention to cause app to freeze while in progress. In that case we need to explore threading.
Core data provides the method
- (void)performBlock:(void (^)())block on
NSManagedObjectContext as a compact wrapper around operations that can be performed on any thread. But the simplicity of this method is deceiving.
If you watch
NSManagedObjectContext for changes in data, you now have to be aware of the thread they are received on. Code that makes changes to your UI such as
UIKit must be on the main thread. If you make changes in core data background threads these notifications can appear from those threads and cause
UIKit ti crash that are almost impossible to trace back.
I have shipped code which actually made this mistake, so have built in protection against it
NSManagedObjectContext to make your changes and submit them back to a parent which you know is tied to the main thread. More on this NSPrivateQueueConcurrencyType
Thread contention & atomic operations
You should confine all core data
performBlock’s to the one serial
NSOperationQueue for safety.
performBlock makes changes to data, there’s no guaratee another
performBlock is not performing changes on a different thread at approximately the same time. When this happens you run the risk of core data returning errors that make no sense. Enqueueing core data operations into a serial
NSOperationQueue will guard against this. I’ve added a short gist to show an example
Objc.io has a nice writeup on the subject. After skimming through it you may come to this conclusion which I strongly recommend: Avoid non light-weight migrations.
This is totally possible if you are a bit creative, and its worth the extra effort. These are my reccommendations:
If a properties data type changes, create a new property instead of modifying the existing one
Changing the datatype will not allow light-weight migrations to happen. This is because CoreData doesn’t know how to modify the existing data to conform to the new datatype. If you create a new property and delete the old one, If you care about keeping the old data you have no choice but to venture into manual migration territory.
Use 2 managed object models
If you have data that you need to keep persistent accross app deployments then it maybe worth keep that in a different datamodel (or outside Core Data). One where migrations are important. Data that is purely an offline cache can be kept separate where migrations are not even considered, the data is killed each new app update.
Use magical record for the right reasons
Magical record is an abstraction layer ontop of Core Data. I use it for my own apps, but still know how to drop down to the level of pure Core Data. There’s many resources for learning more, Apple has an write up of its own which maybe a good place to start
Core Data is worth the headaches
I think its a given that every iOS dev wrestles with Core Data during the course of their career, but as a persistence layer it does a pretty great job of managing many of needs of modern iOS apps.