Getting Started with MagicalRecord

Get started with MagicalRecord, an active record-style library to make your Core Data code cleaner and simpler! By Ed Sasena.

Leave a rating/review
Save for later
Share

Beer ahead!
Photo: Krzysztof Szkurlatowski

Space game

Update note: This tutorial was updated for iOS 8 and Swift by Ed Sasena. Original tutorial was by team member Andy Pereira.

Core Data is Apple’s built-in solution for the persistence and querying of user data in your OS X and iOS apps. Although Apple constantly tinkers with things to make it easier for developers to use, Core Data remains a challenging API to master.

Even when you know how to use Core Data, everyday tasks can feel clunky and cumbersome. Good thing there is MagicalRecord, a third-party library for Core Data created by MagicalPanda. MagicalRecord provides convenience methods that wrap common boilerplate code for Core Data setup, query and update. It takes many design cues from the venerable Active Record design pattern.

This tutorial will get you up to speed with MagicalRecord quickly and easily. You’ll create an app to keep track of your favorite beers (or other such beverage). It will allow you to:

  1. Add a beer.
  2. Rate the beer.
  3. Make notes about the beer.
  4. Take a photo of the beer.

To follow along with this tutorial, you should already have a basic understanding of Core Data. For a review of the basics, check out our introductory Core Data tutorial before moving on.

Getting Started

Start by downloading this Starter Project. Open the project by double-clicking BeerTracker.xcworkspace (not BeerTracker.xcodeproj).

Use xcworkspace file

Run the application and begin checking it out. You might see some build warnings related to MagicalRecord, which you can safely ignore.

BeerTracker_FirstRun

The app has a basic navigation controller with an Add button, table view, search bar and a segmented control for sorting alphabetically or by rating. Tapping the + button exposes the scene for entering and viewing beer information. The app will not save new entries – yet.

Take a look around the project. Expand the BeerTracker target and then the BeerTracker group.

BeerTrackerTarget

While poking around, you’ll notice that a Core Data model exists but the code doesn’t actually use it. Over the course of this tutorial, you’ll update the project to use the model with MagicalRecord without having to build the Core Data infrastructure.

Exploring MagicalRecord

In the Project Navigator, expand the Pods target followed by the PodsMagicalRecord, and Shorthand groups.

Pods target.

In the Shorthand group, find and open the file named NSManagedObjectModel+MagicalRecord.h.
Notice methods in the header are all prefixed with MR_. Since MagicalRecord adds extensions to Core Data classes, this namespacing ensures the added methods won’t conflict with existing names.

However, using this prefix in so many places can be cumbersome. Luckily, MagicalRecord makes it easy to eliminate the prefix with Shorthand Support.

Again, in the Shorthand group, expand the Support Files group, and open Pods-BeerTracker-MagicalRecord-prefix.pch. This is the project’s precompiled header containing two important lines:

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

These two lines are part of what enables MagicalRecord for the project:

  1. MR_SHORTHAND tells MagicalRecord the project will not use the MR_ prefix before any MagicalRecord method name. MagicalRecord+ShorthandSupport.m contains the code to make this work, for those who might be interested in more details.
  2. Importing CoreData+MagicalRecord.h provides access to any of the MagicalRecord APIs.

Beer Model

It’s time to use the data model to start tracking your favorite beers. You can’t expect yourself to remember them, right? To make the data model classes accessible to Objective-C code from the MagicalRecord library, open Beer.swift. Add the following line before the class declaration:

@objc(Beer)

This will ensure the class is visible to the Objective-C runtime and Core Data.

Next, open BeerDetails.swift and perform a similar step – add the following line before the class declaration:

@objc(BeerDetails)

Next, it’s time to initialize the Core Data stack. Open AppDelegate.swift and find application(_:didFinishLaunchingWithOptions:). Add the following lines of code just before the return statement:

MagicalRecord.setupCoreDataStackWithStoreNamed("Beer")

If you’ve ever worked on an app that uses Core Data, you’ve seen how much code it takes to set up and initialize the stack. With MagicalRecord, it only takes only this one line!

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

  • The type of backing store.
  • Enabling or disabling auto-migration.
  • The Core Data Model filename matching or not matching the project name.

The MagicalRecord convenience methods corresponding to the list above are:

  • setupCoreDataStackWithInMemoryStore
  • setupAutoMigratingCoreDataStack
  • setupCoreDataStack

If the data model file has the same base name as the project (e.g., a model file BeerTracker.xcdatamodeld, within a project named BeerTracker), then you can use one of these convenience methods listed above. In this case, the model file is named differently from the project so you need to specify the store name with either setupCoreDataStackWithStoreNamed(_:) or setupCoreDataStackWithAutoMigratingSqliteStoreNamed(_:).

Once you have an initial data model, Core Data requires some additional code to handle small changes made to the model. With the setup methods described as AutoMigrating, MagicalRecord will handle the migration from the old data model to the new model if the migration is possible.

Build and run. There should be no changes to the functionality or look of the app. All changes so far have been behind-the-scenes setup for saving beer information.

Brewing a Beer (entity)

Now that the data model and Core Data stack are set up, it’s time to start adding beers to the list. Open BeerDetailViewController.swift and add the following property to the class:

var currentBeer: Beer!

This will hold a Beer entity to keep track of the currently displayed beer.

Next, find viewDidLoad() and add the following code to the end of the method:

if let beer = currentBeer {
	// A beer exists.  EDIT Mode.
	
} else {
	// A beer does NOT exist.  ADD Mode.
	
	currentBeer = Beer.createEntity() as! Beer
	currentBeer.name = ""
}

When BeerDetailViewController loads, it will be a result of one of two events in BeerListViewController:

  • The user selected a beer from the list – Edit mode
  • The user tapped the + button – Add mode

The code just added checks whether a Beer object is set. If so, it must be edit mode; if not, then the user is adding a new beer and you create a new Beer object instead.

Next, you’ll need to do something similar for the beer details. Add the following code to the end of viewDidLoad():

let details: BeerDetails? = currentBeer.beerDetails
    
if let bDetails = details {
  // Beer Details exist.  EDIT Mode.
} else {
  // Beer Details do NOT exist.  Either ADD Mode or EDIT Mode with a beer that has no details.

  currentBeer.beerDetails = BeerDetails.createEntity() as! BeerDetails
}

This will try to access the beer details, and do the same check as before to go into either edit or add mode.

Build and run to make sure things still compile. There are still no changes to the look of the app, but be patient! Just like a well-crafted brew, well-crafted code takes time.

Ed Sasena

Contributors

Ed Sasena

Author

Over 300 content creators. Join our team.