Search code examples
iosswiftcore-datansmanagedobjectcontextmagicalrecord

Passing an instance of NSManagedObjectContext


I use MagicalRecord to work with core data.

I have a separate class called DatabaseManager to handle core data related tasks. I have a initializer accepting a NSManagedObjectContext object.

import CoreData
import Foundation
import MagicalRecord
import SwiftyJSON

public class DatabaseManager {

    private let context: NSManagedObjectContext!

    init(context: NSManagedObjectContext) {
        self.context = context
    }

    public func insertReports(data: AnyObject, success: () -> Void, failure: (error: NSError?) -> Void) {
        MagicalRecord.cleanUp()
        MagicalRecord.setupCoreDataStackWithStoreNamed("Records")

        let json = JSON(data)
        if let reports = json.array {
            for reportObj in reports {
                let report = Report.MR_createInContext(context) as Report
                report.id = reportObj["Id"].int
                report.type = reportObj["Type"].string!
            }
            context.MR_saveToPersistentStoreAndWait()
            success()
        }
    }

    private func getReport(id: Int) -> Report {
        let idFilter = NSPredicate(format: "id == %@", NSNumber(integer: id))
        let fetchRequest = Report.MR_requestAllWithPredicate(idFilter, inContext: context)
        return Report.MR_executeFetchRequestAndReturnFirstObject(fetchRequest, inContext: context) as Report
    }
}

And I initialize the class and call it's insertReports() method to insert some records into core data.

let context = NSManagedObjectContext.MR_defaultContext()
let dbManager = DatabaseManager(context: context)

getReportsFromAPI({ (data) -> Void in
    dbManager.insertReports(data, success: { () -> Void in
        println("Reports added successfully")
    }, failure: { (error) -> Void in
        println("Error inserting Reports: \(error?.localizedDescription)")
    })
}, failure: { (error) -> Void in
    println("Error getting Reports from API: \(error?.localizedDescription)")
})

But this doesn't seem to work. I get the following output in the console.

2015-03-05 00:39:39.412 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setDefaultContext:](0x104396400) Set Default Context: (null)
2015-03-05 00:39:39.412 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setRootSavingContext:](0x104396400) Set Root Saving Context: (null)
2015-03-05 00:39:39.583 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_contextWithStoreCoordinator:](0x104396400) -> Created Context UNNAMED
2015-03-05 00:39:39.584 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setRootSavingContext:](0x104396400) Set Root Saving Context: <NSManagedObjectContext: 0x7f81b04c6b00>
2015-03-05 00:39:39.584 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_newMainQueueContext](0x104396400) Created Main Queue Context: <NSManagedObjectContext: 0x7f81b04c75a0>
2015-03-05 00:39:39.584 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setDefaultContext:](0x104396400) Set Default Context: <NSManagedObjectContext: 0x7f81b04c75a0>
2015-03-05 00:39:40.434 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setDefaultContext:](0x104396400) Set Default Context: (null)
2015-03-05 00:39:40.434 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setRootSavingContext:](0x104396400) Set Root Saving Context: (null)
2015-03-05 00:39:40.435 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setDefaultContext:](0x104396400) Set Default Context: (null)
2015-03-05 00:39:40.435 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setRootSavingContext:](0x104396400) Set Root Saving Context: (null)
2015-03-05 00:39:40.443 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_contextWithStoreCoordinator:](0x104396400) -> Created Context UNNAMED
2015-03-05 00:39:40.443 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setRootSavingContext:](0x104396400) Set Root Saving Context: <NSManagedObjectContext: 0x7f81b280e5a0>
2015-03-05 00:39:40.444 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_newMainQueueContext](0x104396400) Created Main Queue Context: <NSManagedObjectContext: 0x7f81b04d76c0>
2015-03-05 00:39:40.444 Reports[7123:967179] +[NSManagedObjectContext(MagicalRecord) MR_setDefaultContext:](0x104396400) Set Default Context: <NSManagedObjectContext: 0x7f81b04d76c0>
2015-03-05 00:39:40.446 Reports[7123:967179] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x7f81b04c75a0) → Saving <NSManagedObjectContext (0x7f81b04c75a0): *** DEFAULT ***> on *** MAIN THREAD ***
2015-03-05 00:39:40.446 Reports[7123:967179] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x7f81b04c75a0) → Save Parents? 1
2015-03-05 00:39:40.447 Reports[7123:967179] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x7f81b04c75a0) → Save Synchronously? 1
2015-03-05 00:39:40.448 Reports[7123:967179] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x7f81b04c6b00) → Saving <NSManagedObjectContext (0x7f81b04c6b00): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
2015-03-05 00:39:40.448 Reports[7123:967179] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x7f81b04c6b00) → Save Parents? 1
2015-03-05 00:39:40.448 Reports[7123:967179] -[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x7f81b04c6b00) → Save Synchronously? 1

After that, it just freezes. No error is thrown but the saving operation doesn't get completed either.

I replaced the places where I use the property context with NSManagedObjectContext.MR_defaultContext() and ran the app and the data got saved successfully.

Why can't I pass in an instance of MR_defaultContext() through the initializer?


Solution

  • Your backtrace says it all, you are setting up too many connections to the persistent store. The MagicalRecord documentation details that you should set up your stack either in the App Delegate's didFinishLaunching phase, or in your case, when your DatabaseManager class is instantiated.

    So the call to MagicalRecord.setupCoreDataStackWithStoreNamed("Records") should move to your initializer (possibly safer to place this inside a dispatch_once block to prevent it from being setup more than once)

    init(context: NSManagedObjectContext) {
        struct Static {
            static var onceToken : dispatch_once_t = 0
        }
        dispatch_once(&(Static.onceToken)) {
            MagicalRecord.setupCoreDataStackWithStoreNamed("Records")
        }
        self.context = context
    }
    

    As an aside, you only need to call cleanup when your app is closing, so perhaps in your App Delegate's willTerminate

    All the best with MagicalRecord :)