Trouble with iOS 8 CoreData iCloud sync consistency

738 Views Asked by At

I have a universal app written in Swift using xCode 6.3.2. It is currently very simple in that when I push a button a random number is generated and then stored using CoreData. This works perfectly until I implement iCloud. With iCloud enabled storing a new random number doesn't always propagate onto additional devices. It does most of the time, but not always.

I am testing using an iPad Air, iPhone 6 Plus and iPhone 4s

I am using the following three notification observers:

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "persistentStoreDidChange", name: NSPersistentStoreCoordinatorStoresDidChangeNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "persistentStoreWillChange:", name: NSPersistentStoreCoordinatorStoresWillChangeNotification, object: managedContext.persistentStoreCoordinator)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "receiveiCloudChanges:", name: NSPersistentStoreDidImportUbiquitousContentChangesNotification, object: managedContext.persistentStoreCoordinator)

and here is the function for the third one:

func receiveiCloudChanges(notification: NSNotification)
{
    println("iCloud changes have occured")
    dispatch_async(dispatch_get_main_queue())
        {
            self.activityIndicator.startAnimating()
            self.updateLabel.text = "iCloud changes have occured"

            self.managedContext.performBlockAndWait
                { () -> Void in
                    self.managedContext.mergeChangesFromContextDidSaveNotification(notification)
                }
            self.reloadTextViewAndTableView()
            self.activityIndicator.stopAnimating()
        }
}

I am not attempting to update the UI until the managedContext is finished with the merge, and I am performing everything on the main thread. I am really at a loss why the changes on one device are only displayed on the second or third one about 90-95% of the time.

As part of my trial and error, when I went to delete the app from my test devices and reinstall there is sometimes a message that an iCloud operation is pending, but it doesn't matter how long I wait, once the devices are out of sync they stay that way. Even when they are out of sync if I add another number or two those will still propagate to the other devices, but then I will invariably lose more data. It seems to work about 90% of the time.

I use the following to update the UI:

func reloadTextViewAndTableView()
{
    let allPeopleFetch = NSFetchRequest(entityName: "Person")
    var error : NSError?
    let result = managedContext.executeFetchRequest(allPeopleFetch, error: &error) as! [Person]?

    //Now reload the  textView by grabbing every person in the DB and appending it to the textView
    textView.text = ""
    allPeople = managedContext.executeFetchRequest(allPeopleFetch, error: &error) as! [Person]
    for dude in allPeople
    {
        textView.text = textView.text.stringByAppendingString(dude.name)
    }
    tableView.reloadData()
    println("allPeople.count = \(allPeople.count)")
}

I am really at a stand still here. I am just not sure why it "usually" works...

1

There are 1 best solutions below

0
On BEST ANSWER

So I am still not sure how or why CoreData is sometimes getting out of sync as described above. I have found though that if I enter one new number after another very rapidly is when it usually occurs.

As a workaround I have added a button to the UI that allows the user to force a resync with iCloud by rebuilding the NSPersistentStore using the following option.

NSPersistentStoreRebuildFromUbiquitousContentOption: true

I would much rather the store stayed in sync with all other devices all the time, but at least this way the user will never lose data. If they notice that they are missing a record they know they entered on another device, then all they have to do is hit the resync button.