Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the new way of binding an NSArrayController to the managed object context of a Core Data document?

Before Xcode went and added Storyboards for OS X apps you could connect an array controller to your document's managed object context by binding the Managed Object Context of the array controller to File's Owner with a Model Key Path of managedObjectContext. With storyboards there is no more File's Owner so where do you get the context from now?

Apple's documentation is behind in this area and there aren't any obvious places to bind to in Xcode. Obviously I can just fall back to a non-storyboard route and use the old method, but there must be a new way of doing it.

like image 860
theMikeSwan Avatar asked Jan 28 '15 03:01

theMikeSwan


3 Answers

Using the default Xcode generated project and including CoreData puts the managedObjectContext member on the AppDelegate. You can add the following code to your ViewController, then use managedObjectContext as the "Model Key Path" with binding to ViewController for your NSArrayController.

lazy var managedObjectContext: NSManagedObjectContext = { 
    return (NSApplication.sharedApplication().delegate
        as? AppDelegate)?.managedObjectContext }()!

This simply creates a member which redirects to where your actual MOC is stored. This is useful because the NSArrayController binding happens before viewDidLoad(), hence why an instance member will not suffice. Also, if you want to refactor to a singleton CoreDataManager class, you can just change where to redirect to. Additionally, you could add this as a class extension to enable all ViewControllers to access your MOC.

Objective-C version upon request:

@interface MyViewController ()

@property (nonatomic, readonly) NSMangedObjectContext* managedObjectContext;

@end

@implementation MyViewController

- (NSManagedObjectContext*)managedObjectContext
{
    return ((AppDelegate*)([NSApplication sharedApplication].delegate)).managedObjectContext;
}

...

@end
like image 155
Sandy Chapman Avatar answered Nov 22 '22 10:11

Sandy Chapman


So I have the answer from Apple. This is for Document based Core Data apps, the code is all in Swift but the idea is the same in Objective-C you just have to translate it.

The first answer they gave me was to bind the array controller to the view controller running the view with a model key path of self.view.window.windowController.document.managedObjectContex. The sample I was shown used this method and had no error messages at all, it was however a single view controller inside the window controller with one array controller. My setup is a window to a tab view to the views with two array controllers in the one scene. I was still getting Cannot perform operation without a managed object context once each time a new document was opened or created. The second solution, that worked for me was to still bind the array controller to the view controller but with a model key path of self.representedObject.managedObjectContext and then to add to the end of the document class's makeWindowControllers() function:

override func makeWindowControllers() {
……
    let tabViewController = windowController.contentViewController as NSTabViewController
    for object in tabViewController.childViewControllers {
        let childViewController = object as NSViewController
        childViewController.representedObject = self
    }
}

This solved the issue for me. Hopefully there is enough info here to show up when others google this issue.

like image 38
theMikeSwan Avatar answered Nov 22 '22 10:11

theMikeSwan


You have always been able to bind through NSApplication with a keypath of delegate.managedObjectContext if the application delegate owns the core data stack. Otherwise you could pass pass the MOC through to each view controller with a MOC property on each one, which is strongly preferred by those who argue that the app delegate shouldn't be used to own singleton MOCs, and that there's further utility in being able to provide each VC a separate MOC.

I believe you could also create a MOC instance in the storyboard in IB. There's also always been a MOC object for nibs, at least. Though I haven't used that enough to know how it relates to a programmatic core data stacks. Probably better to just have a MOC property somewhere you can access in either the VC hierarchy or app delegate

like image 20
stevesliva Avatar answered Nov 22 '22 11:11

stevesliva