Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I access and use an entity using Core Data

It's time to use Core Data, but the open documentation and guides out there spend a lot of time talking about general setup and nitty gritty "behind the scenes" details. Those things are important, but I would love a quick, clean source showing how to actually use information stored in a Core Data model.

The Scenario

In my simple example I have a single entity type:

Job
 - salary [Double]
 - dateCreated [Date]

This is a Swift iOS app powered by story boards, with the default generated AppDelegate.swift which handles the generation of my Managed Object Context.

The Question

How do I use Job instances in my application?

Bonus points if you can also provide insight around these items:

  • As someone who is used to the MVC design pattern, how do I avoid including dirty data access inside of my controllers without bucking iOS development best practices?
  • How can I access entities from Core Data while following DRY?
  • How do I pass managed objects between methods and controllers while maintaining their type?

The Core Data documentation provides some snippets for fetching records. This question is essentially asking where that logic belongs in an iOS application, and how to actually interact with the fetched records after fetching them.

An Example

This question isn't meant to be a broad, sweeping question so I will ground it in an example attempt to use Core Data. In my example I have a single UIViewController which has a label. I want this label to show the salary from a job.

import UIKit
import CoreData

class JobViewController: UIViewController {
    
    @IBOutlet var salaryLabel: UILabel!
    let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
    
    func updateLabel() {
        var job = getCurrentJob()
        salaryLabel.text = job.salary // ERRORS
    }
    
    func getCurrentJob()->(???) {
        var error: NSError?
        if let fetchedResults = managedObjectContext!.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: &error) {
            return fetchedResults[0]
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

This example will not compile for two reasons:

  1. I didn't specify a return type for getCurrentJob, since I wasn't sure what type to return
  2. The line with "ERRORS", which tries to access the salary attribute, will error because there is no way of knowing that salary is an actual attribute of job.

How do I pass around and use the job object?

like image 942
slifty Avatar asked Mar 07 '15 16:03

slifty


2 Answers

The key missing piece from the above example are NSManagedObject subclasses, and in Swift, the @NSManaged Swift annotation. NSManagedObject is a generic class which, in its most simple form, can be extended to simply provide access to attributes of a Core Data entity, but in reality this is where traditional model logic should live.

Creating NSManagedObject Subclasses

You can automatically generate these objects by viewing the Core Data model, and using the menu command: Editor->Create NSManagedObject Subclass.

This will generate Job.swift (or whatever your entity name is)

import Foundation
import CoreData

class Job: NSManagedObject {

    @NSManaged var dateCreated: NSDate
    @NSManaged var salary: NSNumber

}

Using NSManagedObject Subclasses

Your new class is now available for use, and you can typecast the fetched result accordingly! For completion, here's the updated version of the previously broken example

import UIKit
import CoreData

class JobViewController: UIViewController {

    @IBOutlet var salaryLabel: UILabel!
    let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext

    func updateLabel() {
        var job:Job = getCurrentJob()
        salaryLabel.text = job.salary // ERRORS
    }

    func getCurrentJob()->Job {
        var error: NSError?
        if let fetchedResults = managedObjectContext!.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: &error) {
            return fetchedResults[0]
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
like image 52
slifty Avatar answered Sep 21 '22 21:09

slifty


For instance, create a Core Data model like this:

enter image description here

Now generate Swift source code from it (with Editor | Create NSManagedObject Subclass). This will allow you to compile the following version of JobViewController (which currently lacks error handling and more):

import UIKit
import CoreData

class JobViewController: UIViewController {

    @IBOutlet var salaryLabel: UILabel!
    let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
    var jobs: [Job] = []

    override func viewDidLoad() {
        super.viewDidLoad();

        jobs = managedObjectContext.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: nil) as [Job];
    }

    func updateLabel() {
        salaryLabel.text = "\(jobs[0].salary) $"
    }
}
like image 33
Drux Avatar answered Sep 18 '22 21:09

Drux