MagicalRecord Tutorial for iOS

A MagicalRecord tutorial for iOS that introduces the basics of setting up a CoreData stack and working with this popular library. By Andy Pereira.

Leave a rating/review
Save for later
Share

Beer ahead!
Photo: Krzysztof Szkurlatowski

Space game

Beer ahead!
Photo: Krzysztof Szkurlatowski

Update: This tutorial has been updated for iOS 8 and Swift here. This Objective-C version is no longer supported but is here for reference.

For many years, Core Data has been an integral part of many OS X and iOS apps, supporting the persistence and querying of user data. Apple constantly tinkers with Core Data API, in an effort to make it easier for developers to use and integrate into apps.

That said, the truth is that Core Data remains a challenging API to master. Even when you know how to use Core Data, mundane, everyday tasks can feel clunky and cumbersome. Good thing there is MagicalRecord, a third-party library for Core Data created by MagicalPanda – and good that this MagicalRecord tutorial is focused on bringing you up to speed with MagicalRecord quickly and easily :]

MagicalRecord is easy to use, well developed and popular. As described by the project’s author, the point of MagicalRecord is to “clean up” Core Data code and enable simple one-line fetches of data, while still allowing custom performance optimizations. How does it do this? It provides convenience methods, which wrap common boilerplate for Core Data setup, query and update. Its design also features influences from Ruby on Rails’ ActiveRecord persistence system (an instance of the venerable Active Record design pattern).

But enough theory! Just follow along this MagicalRecord tutorial to see how MagicalRecord works its, well, magic.. In this tutorial, you’ll create an app that will keep track of your favorite beers. It will allow you to:

  1. Add a beer of your liking
  2. Rate the beer
  3. Make notes about the beer
  4. Take a photo of the beer – in case you’ve had one too many to make sense of your notes

Getting Started

To follow this tutorial, you should already have a basic understanding of Core Data, at the level provided by a basic Core Data tutorial. Beyond that, you don’t need any prior experience with Core Data.

If you lack even a basic experience with Core Data it’d be great if you checked first this introductory Core Data tutorial and then come back here.

To begin, start by downloading this Starter Project. Once you have it, go ahead and run the application so you can check it out.
BeerTracker_FirstRun

It has a basic navigation controller with an Add button, table view, search bar (if you pull down on the table) and a segmented control for sorting alphabetically or by rating. If you click the Add button you’ll go to the UI, where you’ll enter and view beer information. If you try to enter anything, it won’t save – yet.
Now take a look around the code. In the Project Navigator you’ll see:

  • All of the view controllers that you use to make the application work
  • An ImageSaver utility class
  • Images that you’ll use later for populating initial data
  • An Images.xcassets library that has a few images that are used by the UI
  • AMRating – an easy to use third-party library for a rating control
  • And finally, MagicalRecord

While you’re poking around, you may notice there is no Core Data model, nor does AppDelegate.m contain any code to set it up. This is the perfect scenario for any project you started without Core Data, only to realize later that Core Data is the way to go.

Exploring MagicalRecord

In the Project Navigator, go ahead and expand the MagicalRecord group. Inside, you’ll find groups for Categories and Core, as well as CoreData+MagicalRecord.h. Expand the Categories group, and open the file named NSManagedObjectModel+MagicalRecord.h.
You’ll notice that the methods in the header are all prefixed MR_. In fact, if you go through any of the files in the Categories group, you’ll notice all of the methods are prefixed with MR_.
I don’t know about you, but I find it rather confusing to begin every method with the same two letters. Good thing MagicalRecord makes it easy to change them up with Shorthand Support.

Again, in the Project Navigator, expand the Supporting Files group, and open BeerTracker-Prefix.pch. This is the precompiled header for the project. The starter project has added two lines to this file:

#define MR_SHORTHAND
#import “CoreData+MagicalRecord.h”

These two lines enable MagicalRecord for your project:

  1. MR_SHORTHAND tells MagicalRecord that you do not want to have to type MR_ before any MagicalRecord method. You can look in MagicalRecord+ShorthandSupport.m to find out more how this works. As it goes beyond the scope of this tutorial, it will not be discussed here.
  2. By importing CoreData+MagicalRecord.h, you can now access any of the MagicalRecord APIs in any of your project files without having to import them in every file you need them.

Note:If you want to add MagicalRecord to your own project, there are a few tips on their GitHub page. You can also add MagicalRecord the same way it I added it to this project:

  1. Download the MagicalRecord project from GitHub
  2. Drag the MagicalRecord directory (that contains CoreData+MagicalRecord.h, Categories, and Core) into your Xcode project
  3. Make sure CoreData.framework is added to your project settings under Build Phases\Link Binary With Libraries
  4. In your precompiled header, add the following within #ifdef __OBJC__:
    #define MR_SHORTHAND
    #import “CoreData+MagicalRecord.h”
            

Note:If you want to add MagicalRecord to your own project, there are a few tips on their GitHub page. You can also add MagicalRecord the same way it I added it to this project:

  1. Download the MagicalRecord project from GitHub
  2. Drag the MagicalRecord directory (that contains CoreData+MagicalRecord.h, Categories, and Core) into your Xcode project
  3. Make sure CoreData.framework is added to your project settings under Build Phases\Link Binary With Libraries
  4. In your precompiled header, add the following within #ifdef __OBJC__:
    #define MR_SHORTHAND
    #import “CoreData+MagicalRecord.h”
            

#define MR_SHORTHAND
#import “CoreData+MagicalRecord.h”
        

Beer Model

To start keeping track of your favorite beers, you’re going to need a model, because you can’t expect yourself to remember them, right? From the Xcode menu, select File\New\File…. From the list on the left hand side, select Core Data and choose Data Model from the options.
Create Data Model

Name the file BeerModel.xcdatamodeld and put it in the BeerTracker group.

In the project navigator, select the new bundle BeerModel.xcdatamodeld to start editing it. Add an entity named Beer. Add an attribute named name with a type of String.
Add Beer Entity

Add another Entity named BeerDetails. This entity will track of the details for a beer, such as user rating, notes and where to find the image. Add the following attributes to BeerDetails, with the corresponding types:

  • Attribute: image Type: String
  • Attribute: note Type: String
  • Attribute: rating Type: Integer 16

Add BeerDetails Entity

Next, you’re going to create a relationship between the two, so the Beer entity knows which BeerDetails belong to it. Under BeerDetails, create a new relationship and name it “beer” with the destination Beer. By selecting the destination beer, you’ve established the first part of the relationship between the entities.
Add Beer Relationship

Finish setting up the relationship by selecting the Beer entity. Add a relationship named beerDetails, with the destination of BeerDetails, and the inverse of Beer. Now your Beer entity will have a BeerDetails entity to call its own.
Add BeerDetails Relationship

The next step is to create data classes to represent the entities. You’ll use Xcode for this, but you need to be careful because of some quirks in Xcode’s behavior.

First, edit the Core Data model by selecting it in the Xcode project navigator; ensure that you highlight the Beer entity in the “ENTITIES” pane — and NOT the BeerDetails entity.

Next, in the Xcode menu, go to Editor\Create NSManagedObject Subclass…. Check BeerModel, then select Next. Under Entities to Manage, select the checkboxes for both the Beer and BeerDetails, if not selected by default, and double-check that Beer is highlighted. Press Next and then Create. This will generate new classes of Beer and BeerDetails corresponding to the entities with those names. Take a moment to look at the new classes, and you’ll see that each class has properties corresponding to the entity attributes you defined.

In particular, check the properties that represent the relationship between entities. You should see that the Beer class holds a property beerDetails of type BeerDetails * object. But, you’ll also see that the BeerDetails class holds a property beer of type NSManagedObject*. Why doesn’t the property have a type of Beer *? This is due to a limitation in Xcode’s “Create NSManagedObject Subclass” command. It has no effect on this project, so feel free to ignore it.

However, that command is not only generating the property types of the classes. It is also generating the class names used to define those types, and the command is not clever enough to do both things fully. One workaround is to simply tweak the generated code and set the property to the precise subclass type (adding forward declarations as needed). Another workaround is just to give Xcode less to do. If you explicitly define the class name for each entity in the Data Model Inspector before generating the classes themselves, then Xcode will generate correct property types.

If you’re curious about more elaborate tools for generating classes from Xcode data models, one option is mogenerator.

Note: Tortured by curiosity? Wondering why Xcode does not create a Beer * property? It appears to be the result of a limitation in Xcode’s CreateNSManagedObjectSubclass command. Since Xcode can see the Core Data Model, the command should logically be able to infer that each class’s property should have the type of the other class in the mutual relationship.

However, that command is not only generating the property types of the classes. It is also generating the class names used to define those types, and the command is not clever enough to do both things fully. One workaround is to simply tweak the generated code and set the property to the precise subclass type (adding forward declarations as needed). Another workaround is just to give Xcode less to do. If you explicitly define the class name for each entity in the Data Model Inspector before generating the classes themselves, then Xcode will generate correct property types.

If you’re curious about more elaborate tools for generating classes from Xcode data models, one option is mogenerator.

Now that you’ve created the data object classes, it’s time to initialize the Core Data stack. Open AppDelegate.m, and in application:didFinishLaunchingWithOptions: add the following lines of code just before the return statement:

// Setup CoreData with MagicalRecord
// Step 1. Setup Core Data Stack with Magical Record
// Step 2. Relax. Why not have a beer? Surely all this talk of beer is making you thirsty…
[MagicalRecord setupCoreDataStackWithStoreNamed:@"BeerModel"];
    

If you’ve ever worked with an Xcode project created with Core Data enabled, you’ve likely seen how much code it takes to setup Core Data initialize it within the AppDelegate file. With MagicalRecord, all you need is that one line!

MagicalRecord provides a few alternative methods for setting up your Core Data stack, depending on the following:

  • Your type of backing store
  • On whether you want auto-migration
  • And on the name of your Core Data Model file

If your model file has the same base name as your project (e.g., a model file BeerTracker.xcdatamodeld, within a project named BeerTracker), then you can use one of MagicalRecord’s first three convenience methods–setupCoreDataStack, setupCoreDataStackWithInMemoryStore, or setupAutoMigratingCoreDataStack.
But, since your model file is named differently, then you have to use one of the other two setup methods, setupCoreDataStackWithStoreNamed: or setupCoreDataStackWithAutoMigratingSqliteStoreNamed:.

With the setup methods described as AutoMigrating, you can change your model, and if it is possible to auto migrate your store, MagicalRecord will handle it for you. Normally, Core Data requires you to add some code to handle small changes you make to your model.

Contributors

Over 300 content creators. Join our team.