Search code examples
macoscore-datansmanagedobjectcontextcocoa-bindingsnsobjectcontroller

How to use NSObjectController and Managed Object Context using Cocoa Bindings


Searched entire Internet but couldn’t find the modern solution for my problem. I want to use NSObjectController in pair with Core Data through Cocoa Bindings and struggle to set it up properly. Worth noting that I’m using latest version of Xcode and Swift.

What I’ve done:

For testing purposes I’ve done the following:

  1. Created an macOS app with “Use Core Data” option selected (the app is not document based);
  2. Dragged 2 NSTextFields into the Storyboard Dragged NSObjectController to the view controller scene;
  3. Added Employee Entity to Core Data model with 2 attributes “name” and “surname”;
  4. Done everything from the answer in How do I bind my Array Controller to my core data model?
  5. Set NSObjectController to entity mode and typed in “Employee”,
  6. Prepares Content selected, Use Lazy Fetching selected so all three options checked;
  7. Binded the NSObjectController’s Managed Object Context in bindings inspector to the View Controller’s managedObjectContext;
  8. Binded NSTextFields as follows: Value - Object Controller, Controller key - selection, Model Key Path - name (for 1st text field) and surname (for 2nd).

That’s it.

First set of questions: What I did wrong and how to fix it if it’s not completely wrong approach?

I’ve read in some post on stackoverflow that doing it that way allows automatic saving and fetching from Core Data model. That’s why I assumed it should work.

So here is a Second set of questions: Is it true? If it is then why text fields are not filled when view is displayed? If it is not then how to achieve it if possible (trying to write as less code as possible)?

Third question: If I used approach that is completely wrong would someone help me to connect Core Data and NSObjectController using Cocoa bindings and show me the way of doing so with as less code written as possible using the right approach?

Taking into account that there no fresh posts about this topic in the wilds I think the right answer could help a lot of people that are developing a macOS app.

Thanks in advance!


Solution

  • I think your basic approach is correct, although it is important to understand that you need a real object, an instance, in order for it to work.

    Creating a NSManagedObject subclass is generally desirable, and is almost always done in a real project, so you can define and use properties. You can do it easily nowadays by selecting the data model in Xcode's Project Navigator and clicking in the menu: Editor > Create NSManagedObject Subclass…. Technically it is not necessary, and in a demo or proof-of-concept, you often muddle through with NSManagedObject.

    Assuming you are using the Xcode project template as you described, wherein AppDelegate has a property managedObjectContext, the following function in your AppDelegate class will maintain, creating when necessary, and return, what I call a singular object – an object of a particular entity, in this case Employee, which your app requires there to be one and only one of in the store.

    @discardableResult func singularEmployee() -> NSManagedObject? {
        var singularEmployee: NSManagedObject? = nil
        let fetchRequest: NSFetchRequest<NSManagedObject> = NSFetchRequest(entityName: "Employee")
        let objects = try? self.managedObjectContext.fetch(fetchRequest)
        singularEmployee = objects?.first
    
        if singularEmployee == nil {
            singularEmployee = NSEntityDescription.insertNewObject(forEntityName: "Employee", into: self.managedObjectContext)
        }
        return singularEmployee
    }
    

    Then, add this line of code to applicationDidFinishLaunching

        singularEmployee()