Search code examples
iosswiftcore-datacloudkittoday-extension

How to access Core Data/CouldKit via Today Extension (iOS8)


I have an app that uses Core Data with the standard Apple Core Data stack in the AppDelegate. I've modified the stack so that Core Data is enhanced with CloudKit. All data is synced across all devices nicely. So far so good.

I'd like to add a Today Extension to my app but I have no idea how to access the data. I've been reading about the AppGroup concept but I also read that I shouldn't/can't use that to access data from the Today Extension. So I figured I should be using CloudKit as I learned that NSManagedObjects get converted to CKRecords upon upload to iCloud.

How exactly do I access the data via iCloud that has been stored by Core Data using CloudKit enhancement? Where exactly is that data stored in the iCloud?

While all data is syncing nicely, when I check in the iCloud Dashboard no data seem to exist. All the SO articles I read seem to be addressing only a part of my problem.

Here is the modified Core Data stack I'm using in my app:

    // MARK: - Core Data stack

lazy var managedObjectModel: NSManagedObjectModel = {
    // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
    let modelURL = NSBundle.mainBundle().URLForResource("AppName", withExtension: "momd")!
    return NSManagedObjectModel(contentsOfURL: modelURL)!
    }()

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
    // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
    // Create the coordinator and store
    var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

    let documentDirectory = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as! NSURL

    let storeUrl = documentDirectory.URLByAppendingPathComponent("AppName.sqlite")

    var error: NSError? = nil
    var failureReason = "There was an error creating or loading the application's saved data."

    let storeOptions = [NSPersistentStoreUbiquitousContentNameKey:"ItemsCloudStore"]

    let url = storeUrl

    if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: storeOptions, error: &error) == nil {
        coordinator = nil
        // Report any error we got.
        var dict = [String: AnyObject]()
        dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
        dict[NSLocalizedFailureReasonErrorKey] = failureReason
        dict[NSUnderlyingErrorKey] = error
        error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
        // Replace this with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog("Unresolved error \(error), \(error!.userInfo)")
        abort()
    }

    return coordinator
    }()

lazy var managedObjectContext: NSManagedObjectContext? = {
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
    let coordinator = self.persistentStoreCoordinator
    if coordinator == nil {
        return nil
    }


            var managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)

    managedObjectContext.persistentStoreCoordinator = coordinator
    return managedObjectContext
    }()


// MARK: - Core Data Saving support
func saveContext () {
    if let moc = self.managedObjectContext {
        var error: NSError? = nil
        if (moc.hasChanges && !moc.save(&error)) {

            println("Unresolved error \(error), \(error!.userInfo)")
            abort()

        } else {

            println("Context was updated")
        }
    }
}

Solution

  • After some research it seems that this is an unsolved problem by Apple. Core Data and iCloud don't work together with Extensions. Check out this article:

    https://devforums.apple.com/message/1050891#1050891

    Bit of a bummer. Let hope that they fix it in iOS9 at least.