Paul Sturgess

Using MagicalRecord and Core Data in RubyMotion

MagicalRecord is a wrapper around Apple’s Core Data Framework. Written in Objective-C,
it’s one of the most popular and mature libraries for working with Core Data.

I really like it as it simplifies a lot of the code, whilst
it still allows you to ‘get your hands dirty’ when necessary.

This article details how I’m using it. This is by no means the ‘perfect’ solution,
as I am evolving it all the time, but it is working well for me. By all means
get in touch if you think I’m missing any obvious tricks.

One thing I have found is that it’s pretty much impossible to hide the fact you
are using CoreData. Particularly with the requirement of a different context for each
thread. But what I do want to do is make things as simple, consistent and maintainable
as possible.

Wrapping MagicalRecord

First I have a Database class to wrap common MagicalRecord tasks.

The main reason for this class is so that I do not sprinkle MagicalRecord calls
all around my code. Having them all in one place will make it easy to update if/when
the MagicalRecord api changes. It also encourages consistency in my usage of MagicalRecord
as, for example, there are many ways to persist your data.

In AppDelegatedidFinishLaunchingWithOptions I call Database.loadOrCreate. As the name implies, this
will either load my existing Core Data stack or it will set up a new one.

I also cleanup the database when my app closes via this method in the AppDelegate:

123

def applicationWillTerminate application
Database.cleanUp
end

Entities

I create my Core Data entities in Xcode (although I do intend to look at the ruby-xcdm gem so I can stop using Xcode). I’m now using the ruby-xcdm gem to define my schema in code. Here’s a blog post about it.

For now, the ib gem is great for allowing us to fire up Xcode just when we need it.
The gem is mostly geared around using Interface Builder but I don’t use it for that.

Once you’ve installed the gem run:

$ rake ib:open

This will open Xcode. Inside the Resources folder on the left hand side there
will be a .xcdatamodeld file. Select this and you can create your entities.

One thing to remember is to set the Class of each Entity to the corresponding Class
in your app. Otherwise it will be a standard NSManagedObject.

So for each Entiy I create a corresponding class like so. Note it inherits from my
own CustomNSManagedObject.

123

class ToDoItem < CustomNSManagedObject
# more methods go here
end

I give each Entity created_at and id attributes in order to help with querying.

Essentially this class gives me ActiveRecord like behaviour. Also, like my Database class,
it ensures I contain some more MagicalRecord calls to a single place.

This means I can do:

ToDoItem.build(
{
id: 1,
action: "Lorem ipsum dolor sit amet"
}
)

My build method automatically inserts a created_at for every new record.

Data store

By default MagicalRecord.setupAutoMigratingCoreDataStack will use SQLite to
persist your data.

Note that I don’t include a save method in CustomNSManagedObject. Saving an individual
record isn’t the most efficient way to persist changes in Core Data. Everything is held
in memory until the relevant context is told to save. Hence why I’ve put the
save method in the Database class.

Persisting data on the main thread

If you’ve used any of the CustomNSManagedObject methods like find, all, or build
without passing in a context then using Database.save_on_main_thread! will persist any
changes made. For example:

Note most of the class methods in CustomNSManagedObject take an optional context so they
can be used on background threads.

Testing

For tests I use the in memory data store and when saving it is synchronous. This ensures the data is there before the assertions!

For example:

123456789101112131415161718192021

describe "SomeTest" do
before do
Database.cleanUp
Database.createTestDB
@localContext = Database.defaultLocalContext
end
describe "some_method" do
context "when some scenario" do
it "should assign something is true" do
# ...
end
end
end
end