Search code examples
swiftcocoacore-datansarraycontroller

Using arrayController results in "Cannot perform operation without a managed object context"


I am rewriting my previous objC app in Swift 2.2. This is cocoa application where I am using NSArrayController to fill NSTableView contents. The error is obvious, although similar setup worked in Objective C app.

Here is my AppDelegate:

 var coreStack:AP_CoreDataStack!
var mainContext:NSManagedObjectContext!

override func awakeFromNib() {
coreStack = AP_CoreDataStack(){ (result) -> () in
        if result {
            self.mainContext = self.coreStack.mainContext
        }
    }
}

Setup of Core Data Stack

// MARK: - AP_CoreDataStack Class
class AP_CoreDataStack {

let mainContext: NSManagedObjectContext
let mastercontext: NSManagedObjectContext
var workerContext: NSManagedObjectContext?


internal typealias CallBack = (result:Bool) -> Void
init ( callback: CallBack) { 


    let modelURL = NSBundle.mainBundle().URLForResource("appNameSWIFT", withExtension: "momd")
    if (modelURL == nil) {
        print("Failed to initialize modelURL: \(modelURL)")
    }

    let mom = NSManagedObjectModel(contentsOfURL: modelURL!)
    if mom == nil {
        print("Failed to initialize model")
    }

    let psc = NSPersistentStoreCoordinator(managedObjectModel: mom!)

    mastercontext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
    mastercontext.persistentStoreCoordinator = psc

    mainContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
    mainContext.parentContext = mastercontext


    // add store to psc in background thread
    let qualityOfServiceClass = QOS_CLASS_BACKGROUND
    let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
    dispatch_async(backgroundQueue, {
        //BACKGROUND THREAD
        // adding store to persistent store coordinator
        let options = [NSInferMappingModelAutomaticallyOption:true,
            NSMigratePersistentStoresAutomaticallyOption:true]
        do {
            // store = try psc.addP
            try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: applicationDocumentDirectory(), options: options)
        } catch let error as NSError {
            print("Error: Failed to load store \(error.localizedDescription), \(error.userInfo)")
        }

        // MAIN THREAD
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            // On Main thread pass message that stack setup is complete
            callback(result: true)
        })
    })


}

Above is the Swift version of my Obj C code which worked fine. I have an NSArrayController in xib file which is bound to Entity and NSManagedObjectContext in IB:

// Bind To Delegate
self.mainContext

It seems Array controller is accessing mainContext before it is initialised, but this is the same setup which worked in objC, so why it is causing error in Swift.

EDIT: I am using regular xib file.

EDIT 2:

Evidently mainContext is not nil as calling it here works correctly

    func applicationDidFinishLaunching(aNotification: NSNotification) {
    // Insert code here to initialize your application

    let request = NSFetchRequest(entityName: "AP_EntityA")
    let list:Array<AnyObject>
    do {
        list = try coreStack.mainContext.executeFetchRequest(request)
        for item in list {
            let product = item as! AP_EntityA
           print("item name is: \(product.uniqueName)")
        }
    } catch let error as NSError {
        // failure
        print("Fetch failed: \(error.localizedDescription)")
    }

}

Solution

  • Add the dynamic keyword to make Swift properties KVO compliant.

    dynamic var mainContext:NSManagedObjectContext!