swiftmacoscore-dataxcode-storyboardnspersistentdocument

NSPersistentDocument, Swift, macOS, and storyboards — how to get the managedObjectContext?


It has been a while since I’ve tackled CoreData and macOS, back in the days of xib and nibs. With a xib, there’s the “File’s Owner” that can give you access to your document and the managedObjectContext. Easy.

With NSPersistentDocument and my storyboard, I’m having a bit of a chicken and egg problem. In my Document class, subclassed from NSPersistentDocument, I have the following:

override func makeWindowControllers() {
    // Returns the Storyboard that contains your Document window.
    let storyboard = NSStoryboard(name: "Main", bundle: nil)
    let windowController = storyboard.instantiateControllerWithIdentifier("Document Window Controller") as! NSWindowController // <- when I need the moc
    self.addWindowController(windowController)
    windowController.contentViewController!.representedObject = self // <- when I set the representedObject
}

This seems to be what many folks, including Apple, are suggesting.

My problem is this: In the MainViewController, I want to have an Object Controller and it needs to be bound to the managedObjectContext but when it needs to have the managedObjectContext, I haven’t yet set the representedObject to self. So an exception is thrown. Setting the representedObject at the end of the makeWindowControllers method is too late but I don’t see anyway to get it in earlier.


Solution

  • Okay. So. I don’t know what was happening last night but there was no way I could get this working.

    This morning, I re-read the documentation on representedObject:

    The representedObject property is key-value coding and key-value observing compliant. When you use the represented object as the file's owner of a nib file, you can bind controls to the file's owner using key paths that start with the string representedObject.

    The docs are clearly telling me that the magic is in representedObject. So I made sure that my makeWindowControllers method was as above and I made sure that my Object Controller in my storyboard was as the docs said it should be.

    Don’t mind the (!)

    I wasn’t surprised that the path has a little (!) because representedObject is just an AnyObject.

    I then dutifully launched the app, fully expecting it not to work.

    But it worked. No idea why yesterday it didn’t but not much I can do about the hours already lost.

    Follow up: As an experiment, I tried one of the twists from yesterday. To get rid of the (!) and to have a reference handy to the moc, I added this method to the MainViewController:

    var moc:NSManagedObjectContext? {
        if let doc = self.representedObject as? Document {
            return doc.managedObjectContext
        }
        return nil
    }
    

    And then I used “self.moc” as the Model Key Path for my Object Controller. This didn’t work and the familiar exception was thrown. Restore the Model Key Path to “self.representedObject.managedObjectContext” and all works nicely. … like magic.