Many iOS developers are familiar with the Core Data library MagicalRecord (MR), written by Saul Mora from Magical Panda Software. After using it during the development Teaching Table, I wanted to share some of my experiences with it.
At a glance
These are the things MagicalRecord sets out to solve for you:
- Easy Fetching of objects
- Multi-threading (background saving)
- iCloud support
- Data Importing
I think the first two points are MagicalRecord’s strongest suit. If you want to simplify a lot of your fetching code, this is the library for you. It also thoroughly provides support for background threading and parent/child contexts, although I personally haven’t tested it fully (more below).
In terms of iCloud support, well, it sets up the basics for you, but it’s well known that, as of this writing, CoreData with iCloud is pretty difficult to implement. (See Daniel Pasco’s talk here or this post). This is obviously not the framework’s fault, but merely a problem it wraps and does not fully address. Basically, just because it supports iCloud, it doesn’t mean you should assume it will work out of the box.
Finally, data importing is a relatively new feature which still needs documentation, but it should allow you to import data from NSObjects. The typical usecase would be importing JSON/XML data into your database. Saul has a post with explanations and examples.
The decision of whether to use Core Data deserves a whole post on its own, so I won’t discuss that here. I’ll assume you’ve done your homework and decided that Core Data is the right fit for your project.
I initially chose to use MR in order to save time writing a lot of boiler-plate code for Core Data. I had worked on several projects with Core Data in the past, and knew how to setup the stack from top to bottom, but found that for this project I had more important things to focus on.
After you import MagicalRecord, all you have to do is choose a stack (in-memory store, auto-migrationg SQLite store, etc), and it does the rest of the work for you.
//Methods from MagicalRecord+Setup.h [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"MyStore"]; //OR [MagicalRecord setupCoreDataStackWithInMemoryStore];
This is done with one of the many class methods of the MagicalRecord class. Most everything else is automatically setup for you (or lazily initialized) including persistent stores, managed object contexts, etc. Obviously, these are pretty standard setups, but if you want something a bit more complex like multiple persistent stores, you would have to add a lot of code yourself.
I think most of the power of MagicalRecord, like other Active Record libraries, comes in its expressiveness. If you wanted to fetch for a set of objects, you normally have to write about 10-15 lines of code. Matt Mallagher saw this early on and wrote some very convenient categories on NSManagedObject to be able to query using a single line:
[[self managedObjectContext] fetchObjectsForEntityName: @"Employee"withPredicate:@"lastName == %@, lastName];
MagicalRecord simplfies this a step further:
[Employee MR_findByAttribute:@"lastName" withValue:lastName];
You’ll notice a few main differences:
- The NSManagedObjectContext (MOC) isn’t specified – MR in this cases uses a default one, although many alternate methods allow you to specify one explicitly.
- In the first approach, the method is operating on the MOC; this is the classical way of fetching in CoreData, which places emphasis on the managed object context. The second approach operates on the Employee model. This shifts the focus to a model-centric design, and, in this case, makes no mention of a MOC at all. It’s important for any iOS developer using CoreData to understand how MOCs work, but this approach does make the code a bit easier to understand when glossing over it. We can see that we’re working with ‘Employees’ instead of directly with a whole dataset.
- This particular method constrains you to a very specific type of query, namely, checking for equality for a particular value (lastName in this case). Again, more general methods are provided, such as MR_findAllWithPredicate:, which gives you all the flexibility you may need, but it’s nice to have some options to make your code more readable and shorter.
In terms of expressiveness, MR provides a whole slew of convenience methods to do just about anything in CoreData – including creating and saving MOCs or even easily initializing your own NSFetchRequests. Its API is pretty easy to follow, and once you work with it for a few days, you’ll be able to save a lot of time and code.
MagicalRecord has been updated to leverage most of iOS 5 Core Data concurrency support (such as private queues, child/parent contexts). In all honestly, I ran into issues early on with this approach, especially when I tried implementing an undo feature with NSUndoManager. Because I don’t have enough experience with these APIs, and the fact that the library was not completely tested for it (as evidenced by recent commits), I decided to completely turn off background threading (for my project). I did this with a few simple changes which you can see here and here. I hope to try the normal threading setup in the future and see if it works well.
I know most of you won’t listen to me, but I would highly suggest using MagicalRecord only once you’ve written a good chunk of Core Data code and understand how it works. Like any other framework, if you understand the technology below it, you can easily resolve any issues that may come up, all the while writing less code and leveraging others’ hard work.