iOS Swift Tutorial: Core Data with MagicalRecord in Swift

iOS Swift Tutorial: Core Data with MagicalRecord in Swift

Hello guys, today you’re going to learn how to use the MagicalRecord extension to Core Data for managing your data in an iOS app in Swift.

First of all, Core Data is far from the only way you could manage your app’s data. There are many discussions about why, where and when to use or not use Core Data for managing your data. One thing that certainly goes to the list of reasons not to use it is its steep learning curve. When I was first starting to grasp the whole Core Data concept I had to spend a few days just learning Core Data as my main focus (going through some written and video tutorials, trying out some sample codes and making some demo apps on my own). Once you figure most of the things out, you start to get some value back of your time and effort investment in learning it. The problem is that almost nobody has any time to waste on pure studying while in the middle of a project. That’s why this tutorial focuses on the usage of a great and widely considered a must use library – MagicalRecord written by Magical Panda Software. This library actually is an extension to the core of Core Data, it adds some convenience methods and functionalities that greatly (maybe even completely) reduce the amount of boilerplate code and simplifies the use of the framework. We highly recommend the usage of MagicalRecord regardless of the experience you have with Core Data.

This tutorial isn’t focusing neither on Core Data, MagicalRecord or even Swift alone, since these are all very extensive concepts and topics. We’re focusing on getting you started as quickly as possible to produce Swift code that uses the MagicalRecord extension to the Core Data framework.

MagicalRecord is written in Objective-C so it’s usage in combination with Swift isn’t quite that seamless as we expect (want) it to be.

Anyway, enough talking, it’s time to get to the dirty bit of actually making something…

  1. Open Xcode (oooh, this was unexpected)
  2. Create an iOS single view application
    1
  3. Name the app to your liking, and make sure “Use Core Data” is unselected. (This isn’t necessary at all, but it’s easier to just add required things than to have to delete unnecessary things)
    2
  4. Great! But the project setup is not over yet. We want the magic from MagicalRecord. You can include the library statically (just get it from GitHub, copy and import it in the project) but I prefer using Cocoapods to dynamically include my required libraries. You can close Xcode now. Make sure you have Cocoapods set up on your Mac.
  5. Create a file named Podfile in the project’s main folder with the following content:
    source 'https://github.com/CocoaPods/Specs.git'
    platform :ios, '8.0'
    pod 'MagicalRecord/Shorthand'
    

    3

  6. Open the terminal. Navigate to your project’s folder. Execute “pod install”. Upon successful finish you’ll have your folder updated with the MagicalRecord library. Now you have to open the .xcworkspace file with Xcode in order to use the project linked with the external libraries (in this case only MagicalRecord).
  7. In Xcode, in the project’s main folder create an Objective-C header file. (Actually the template for the file is not important as long as you set the extension to .h)
    4
    The contents of the file should be:

    #define MR_SHORTHAND
    #import <CoreData+MagicalRecord.h>
    
  8. Name the file using the following convention AppName-Bridging-Header.h. This file is used by the compiler to determine what Objective-C code you want to be available in your Swift files. You tell the compiler this by going to the project’s main target’s build settings, Swift Compiler – Code Generation section and setting the field Objective-C Bridging Header to: AppName/AppName-Bridging-Header.h
    5
  9. In the project’s main folder create a new data model file named the same as the app.
    6
    Create the required entities and relationships shown on the screenshot and be careful to define the delete rule and the type of the detailEntities relationship. (I’ve used the assistant editor to show the same data model file twice, but with separate views for the entities.)
    7
    This represents a simple one-to-many relation which is a fundamental building block in any relational database just to demonstrate the basics of data modelling with Core Data. If you grasp this concept, you’ll be able to manage data models of any complexity.
  10. Next up, while the data model file is selected, go to the Editor section from the menu and select “Create NSManagedObject subclass”. You then select the data model file and the entities for which you want classes to be generated. Make sure you select both of the entities.
    8
    And make sure you select Swift as the language.

    //we use this directive to make the class accessible to Objective-C code from the MagicalRecord library
    @objc(ContainerEntity)
    class ContainerEntity: NSManagedObject {
    
        @NSManaged var name: String
        @NSManaged var detailEntities: NSSet
    
    }
    
    @objc(DetailEntity)
    class DetailEntity: NSManagedObject {
    
        @NSManaged var name: String
        @NSManaged var containerEntity: ContainerEntity
    
    }
    
  11. Now we’re preparing the actual implementation of the app. Let’s define the classes for our two custom view controllers: ContainerEntityVC and DetailEntityVC:
    class ContainerEntityVC: UIViewController, UITableViewDataSource {
        
        @IBOutlet weak var textField: UITextField!
        
        @IBOutlet weak var tableView: UITableView!
        
        var containerEntites: [ContainerEntity]!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            containerEntites = ContainerEntity.findAll() as [ContainerEntity]
        }
        
        @IBAction func saveButtonClicked() {
            let containerEntity = ContainerEntity.createEntity() as ContainerEntity
            containerEntity.name = textField.text
            containerEntites.append(containerEntity)
            NSManagedObjectContext.defaultContext().saveToPersistentStoreAndWait()
            textField.text = ""
            tableView.reloadData()
        }
        
        func tableView(tableView: UITableView,
            numberOfRowsInSection section: Int) -> Int {
                return containerEntites.count
        }
        
        func tableView(tableView: UITableView,
            cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
                let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
                cell.textLabel.text = containerEntites[indexPath.row].name
                return cell
        }
        
        func tableView(tableView: UITableView,
            canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
                return true
        }
        
        func tableView(tableView: UITableView,
            commitEditingStyle editingStyle: UITableViewCellEditingStyle,
            forRowAtIndexPath indexPath: NSIndexPath) {
                if editingStyle == .Delete {
                    containerEntites.removeAtIndex(indexPath.row).deleteEntity()
                    NSManagedObjectContext.defaultContext().saveToPersistentStoreAndWait()
                    tableView.reloadData()
                }
        }
        
        override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
            let vc = segue.destinationViewController as DetailEntityVC
            let containerEntity = containerEntites[tableView.indexPathForSelectedRow()!.row]
            vc.containerEntity = containerEntity
            vc.detailEntites = containerEntity.detailEntities.allObjects as Array
        }
        
    }
    

    class DetailEntityVC: UIViewController, UITableViewDataSource {
        
        @IBOutlet weak var textField: UITextField!
        
        @IBOutlet weak var tableView: UITableView!
        
        var containerEntity: ContainerEntity!
    
        var detailEntites: [DetailEntity]!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            title = containerEntity.name
        }
        
        @IBAction func saveButtonClicked() {
            let detailEntity = DetailEntity.createEntity() as DetailEntity
            detailEntity.name = textField.text
            detailEntites.append(detailEntity)
            containerEntity.detailEntities = NSSet(array: detailEntites)
            NSManagedObjectContext.defaultContext().saveToPersistentStoreAndWait()
            textField.text = ""
            tableView.reloadData()
        }
        
        func tableView(tableView: UITableView,
            numberOfRowsInSection section: Int) -> Int {
                return detailEntites.count
        }
        
        func tableView(tableView: UITableView,
            cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
                let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
                cell.textLabel.text = detailEntites[indexPath.row].name
                return cell
        }
        
        func tableView(tableView: UITableView,
            canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
                return true
        }
        
        func tableView(tableView: UITableView,
            commitEditingStyle editingStyle: UITableViewCellEditingStyle,
            forRowAtIndexPath indexPath: NSIndexPath) {
                if editingStyle == .Delete {
                    detailEntites.removeAtIndex(indexPath.row).deleteEntity()
                    NSManagedObjectContext.defaultContext().saveToPersistentStoreAndWait()
                    tableView.reloadData()
                }
        }
        
    }
    

    I think the code is pretty standard and clear so I won’t go into details in this tutorial but if you have any questions make sure you put them in the comments’ section and I’ll be more than happy to clarify anything that’s nagging you straight away.
    These are the parts where MagicalRecord is used. The differences that using MagicalRecord introduces are:
    – the creation of objects is done by a class method createEntity() provided by the extension. This way the object is created and managed by the Core Data context.
    – the findAll() class method is used to retrieve all objects of the requested class type that exist in the Core Data context.
    – the deleteEntity() instance method is used to remove the object from the Core Data context.
    – you get the default managed object context with NSManagedObjectContext.defaultContext()
    – after every operation on the objects you have to call the saveToPersistentStoreAndWait() (or some other methods used to save the context)

  12. Delete the ViewController class and everything in the Storyboard.
    Disable size classes from the file inspector:
    9
    Setup the Storyboard as shown on the next picture:
    10
    Connect the prototype cell from the ContainerEntityVC to the DetailEntityVC as demonstrated:
    11
    Set the appropriate classes for the view controllers;
    set the view controllers as the data sources for the table views;
    set the prototype cells’ identifiers to “Cell”;
    connect all outlets.
  13. Setup Core Data with MagicalRecord in the AppDelegate:
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
                                
        var window: UIWindow?
    
        func application(application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
            MagicalRecord.setupCoreDataStack()
            return true
        }
    
    }
    

 

And that’s about it. Run and test the app. Remember that if you make any changes to the data model you first have to uninstall the app from the simulator or a device since many Core Data functionalities are set up on the first run of the app. I’ve made this tutorial as generic as possible, not cluttering it with unnecessary extra stuff so that you can focus on the primary goal, learning how to use Core Data with the MagicalRecord extension in Swift.

SwiftMagicalRecordTutorial This is the whole tutorial project. Don’t forget “pod install”. 🙂
Feel free to ask any questions and open discussions about the topic. I hope this tutorial helped you.

Menu Title