Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Sync data between two devices using Core Data & iCloud?

What I am trying to achieve is when user creates, changes or deletes data on the persistent store it will be synced with the iCloud store and then update other devices logged into the same iCloud account.

I have created a Core Data Stack using Objective-C resources and tried to write my own in Swift but I am having problems syncing the data using two devices logged into the same iCloud account.

For e.g. when iDevice A is logged into iCloud it will back up data to iCloud but when iDevice B logs into iCloud the app deletes any data already on the persistent storage to save the iCloud back up, and any changes between the store devices does not appear on the other but seem to save to the iCloud store as the latest back up, so if the app is deleted and reinstalled I see the latest backup made by the other device - with this in mind, if iDevice B was already logged in, it would not use data from iDevice A unless the app was reinstalled and the last back up was made by the other device.

Does anybody know where I am going wrong in the Core data stack to sync the data between two devices using the same iCloud account ?

Core Data Stack:

// MARK: - Core Data stack
// All the following code is in my appDelgate Core data stack 

func observeCloudActions(persistentStoreCoordinator psc:      NSPersistentStoreCoordinator?) {
// Register iCloud notifications observers for;

//Stores Will change 
//Stores Did Change 
//persistentStoreDidImportUbiquitousContentChanges
//mergeChanges

}

//Functions for notifications

func mergeChanges(notification: NSNotification) {
NSLog("mergeChanges notif:\(notification)")
if let moc = managedObjectContext {
    moc.performBlock {
        moc.mergeChangesFromContextDidSaveNotification(notification)
        self.postRefetchDatabaseNotification()
    }
 }
}

  func persistentStoreDidImportUbiquitousContentChanges(notification: NSNotification) {
self.mergeChanges(notification);
 }    

func storesWillChange(notification: NSNotification) {
NSLog("storesWillChange notif:\(notification)");
if let moc = self.managedObjectContext {
    moc.performBlockAndWait {
        var error: NSError? = nil;
        if moc.hasChanges && !moc.save(&error) {
            NSLog("Save error: \(error)");
        } else {
            // drop any managed objects
        }
        moc.reset();
    }

       NSNotificationCenter.defaultCenter().postNotificationName("storeWillChange", object: nil)

 }
}

 func storesDidChange(notification: NSNotification) {
NSLog("storesDidChange posting notif");
self.postRefetchDatabaseNotification();
//Sends notification to view controllers to reload data      NSNotificationCenter.defaultCenter().postNotificationName("storeDidChange", object: nil)

 }

func postRefetchDatabaseNotification() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
          NSNotificationCenter.defaultCenter().postNotificationName("storeDidChange", object: nil)

 })
 }


// Core data stack 

lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "hyouuu.pendo" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1] as! NSURL
}()

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")!
NSLog("modelURL:\(modelURL)")
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 documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last as! NSURL


let storeURL = documentsDirectory.URLByAppendingPathComponent("CoreData.sqlite")

NSLog("storeURL:\(storeURL)")

let storeOptions = [NSPersistentStoreUbiquitousContentNameKey:"AppStore"]

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

if coordinator!.addPersistentStoreWithType(
    NSSQLiteStoreType,
    configuration: nil,
    URL: storeURL,
    options: storeOptions,
    error: &error) == nil
{
    coordinator = nil
    // Report any error we got.
    let dict = NSMutableDictionary()
    dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
    dict[NSLocalizedFailureReasonErrorKey] = failureReason
    dict[NSUnderlyingErrorKey] = error
    error = NSError(domain: "Pendo_Error_Domain", code: 9999, userInfo: dict as [NSObject : AnyObject])
    // 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("AddPersistentStore error \(error), \(error!.userInfo)")
}

self.observeCloudActions(persistentStoreCoordinator: coordinator)

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.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
like image 324
RileyDev Avatar asked Aug 14 '15 21:08

RileyDev


People also ask

What is Core Data CloudKit?

Core Data provides powerful object graph management features for developing an app with structured data. CloudKit lets users access their data across every device on their iCloud account, while serving as an always-available backup service.

What is Core Data swift5?

Core Data is a powerful mobile database allowing developers to create high performance, data-driven iOS and macOS applications. This post presents examples of creating, updating, and deleting Core Data objects in Swift: Defining A New Core Data Entity. a. New Data Model Entity Using Xcode.

What is transformable in Core Data?

When you declare a property as Transformable Core Data converts your custom data type into binary Data when it is saved to the persistent store and converts it back to your custom data type when fetched from the store. It does this through a value transformer.


1 Answers

This how I have mine setup and it syncs my core data and it keeps changes synced.

enter image description here

This from my AppDelegate.

    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 url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("APPNAME.sqlite")
    var error: NSError? = nil
    var failureReason = "There was an error creating or loading the application's saved data."
    // iCloud store
    var storeOptions = [NSPersistentStoreUbiquitousContentNameKey : "APPNAMEStore",NSMigratePersistentStoresAutomaticallyOption: true,
        NSInferMappingModelAutomaticallyOption: true]
    // iCloud storeOptions need to be added to the if statement
    do {
        try coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: NSURL.fileURLWithPath(url.path!), options: storeOptions)
    } catch var error1 as NSError {
        error = error1
        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()
    } catch {
        fatalError()
    }

    return coordinator
}()

    // MARK: - iCloud
// This handles the updates to the data via iCLoud updates

func registerCoordinatorForStoreNotifications (coordinator : NSPersistentStoreCoordinator) {
    let nc : NSNotificationCenter = NSNotificationCenter.defaultCenter();

    nc.addObserver(self, selector: "handleStoresWillChange:",
        name: NSPersistentStoreCoordinatorStoresWillChangeNotification,
        object: coordinator)

    nc.addObserver(self, selector: "handleStoresDidChange:",
        name: NSPersistentStoreCoordinatorStoresDidChangeNotification,
        object: coordinator)

    nc.addObserver(self, selector: "handleStoresWillRemove:",
        name: NSPersistentStoreCoordinatorWillRemoveStoreNotification,
        object: coordinator)

    nc.addObserver(self, selector: "handleStoreChangedUbiquitousContent:",
        name: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
        object: coordinator)
}
like image 51
MwcsMac Avatar answered Oct 13 '22 08:10

MwcsMac