Jono
Jono

Reputation: 3633

Communicating with AppDelegate across scenes (Cocoa, swift)

In Xcode 6 (7 too), I'm looking to find out how to get managedObjectContext and use it in a ViewController in a different scene.

  1. I started a new Cocoa project, clicked 'Use CoreData'
  2. Added an entity in CoreData, generated a managedObject for it
  3. Added an array controller, and bound it to the entity
  4. I want to bind the array controller to the managedObjectContext, but I also want to bind the tableView (in my view controller) to the array controller. They are in different scenes, which seems to mean different namespaces.

What I've tried to do: Set an instance variable in ViewController:

var managedObjectContext: NSManagedObjectContext!

In viewDidAppear() I added:

if let app = NSApplication.sharedApplication().delegate! as? AppDelegate {
    if let context = app.managedObjectContext{
        managedObjectContext = context
    } else {
        print("There was no context available, didn't work")
    }
}

Then I bound the columns of the table to the properties of the entity. And cocoa bindings autocompleted, meaning the context was at least recognized properly.

However when I run it, it fails silently with: 'Cannot perform operation without a managed object context'. When debugging the context is being set as a real object, but I have no idea if it's actually initialized. I looked through the docs and cocoa binding troubleshooting but this seems to be a coredata issue.

(I've looked here: Getting managedObjectContext from AppDelegate but I can't override the normal init in swift)

Upvotes: 0

Views: 930

Answers (2)

stevesliva
stevesliva

Reputation: 5665

For what it's worth, you can bind to the Application in IB, with a model key path of self.delegate.managedObjectContext. This is the quick and dirty way.

Some people argue that this is bad, mostly because they think the app delegate shouldn't have the MOC in the first place. I think the app delegate owning the core data stack is fine.

But I'd warn it's not future-proof. If you want to do something like create a child MOC for the VC, and have an easy "undo" of all the local changes in that scene by skipping the save, you'd end up creating and maintaining the moc property on the VC.

Upvotes: 0

Prontto
Prontto

Reputation: 1681

I made one example for you. I have one Entity, called Person with 2 attributes, name and age. This is my ViewController:

import Cocoa

class ViewController: NSViewController {

    // ManagedObjectContext from AppDelegate
    lazy var moc: NSManagedObjectContext = {
        let appDel = NSApplication.sharedApplication().delegate as! AppDelegate
        return appDel.managedObjectContext
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Populate some sample data
        let firstPerson = NSEntityDescription.insertNewObjectForEntityForName("Person", inManagedObjectContext: moc) as! Person
        firstPerson.name = "Jesse Pinkman"
        firstPerson.age = 25

    }
}

And in IB i have one table view and one array controller. Set array controller's entity to Person:

And the bound your array controller's managed object context to your viewcontroller's managed object context, in my example self.moc: enter image description here

And then just bound your table view's colum's to your array controller.

Upvotes: 0

Related Questions