Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data + iCloud Sync did change stores notification doesn't refresh UI

I can't handle this problem, I'm trying to integrate iCloud and Core Data to my app and I stuck with iCloud synchronization part.

My complete scenario:

  1. Local core data storage seeded with initial data
  2. Later the app ask the user about iCloud or local data stoge
  3. If user choose iCloud, current local storage migrates to iCloud store
  4. After migration, context and persistent store coordinator is reloaded with iCloud Store
  5. Refetch data from new context (Trouble here)

If we take away discussion about migration and focus on loading persistent store coordinator with iCloud, I think that the problem is related to
NSPersistentStoreCoordinatorStoresDidChangeNotification event.

I just don't understand it. In every article that I read, I saw something like: "reload you UI here". But it doesn't work for me.

Reload coordinator function

func configureContext() -> NSManagedObjectContext? {
    // Create the coordinator and context
    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

    //remove all observers from "previous" coordinator
    deregisterForStoreChanges()
    //and register them for new one
    registerObservers(persistentStoreCoordinator: coordinator)

    do {

        //storeSettings.url = applicationDocumentsDirectory.URLByAppendingPathComponent("iCloud.sqlite")
        //storeSettings.options = [NSPersistentStoreUbiquitousContentNameKey:"iCloudProject"]

        try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeSettings.url, options: storeSettings.options)

    } catch {
        //Here was standard error handler I didn't changed it, but removed for listing
        abort()
    }
    persistentStoreCoordinator = coordinator

    let managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = coordinator

    //not quite sure about the next string, didn't test it yet
    managedObjectContext.mergePolicy = NSMergePolicy(mergeType:NSMergePolicyType.MergeByPropertyStoreTrumpMergePolicyType )

    //tried to refetch my records here but, with no luck.
    //refetchUIRecords()
    return managedObjectContext
}

And my NSPersistentStoreCoordinatorStoresDidChangeNotification event func

func persistentStoreCoordinatorDidChangeStores(notification:NSNotification){

    if notification.userInfo?[NSAddedPersistentStoresKey] != nil {
        print("-----------------------")
        print("ADDED")
        print("-----------------------")
        //deduplicateRecords()

        //if iCloud -> refetch UI
        if NSUserDefaults.standardUserDefaults().boolForKey("iCloud"){
            self.createTimer()
        }
    }
    print("Did Change")

}

Create timer function is just a function which wait for 1 secods before actual refetching and refreshing UI.

The problem

When we reach step 4 from my scenario, and call configureContext function, I see this in the console:

2016-04-12 13:31:27.749 TestingiCloudProject[2052:1142902] -[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](898): CoreData: Ubiquity:  mobile~567404C0-9D84-4C07-A0F8-D25832CB65D8:iCloudProject
Using local storage: 1 for new NSFileManager current token <ef7a917f bca47ecf 5d58862d cbe9998d 7e53e5ea>
Did Change
Did Change
-----------------------
ADDED
-----------------------
Timer
Did Change
Refetch
Refetching...
Number of records after fetch: 1 //must be more than 1 
2016-04-12 13:31:30.090 TestingiCloudProject[2052:1143055] -[PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:](898): CoreData: Ubiquity:  mobile~567404C0-9D84-4C07-A0F8-D25832CB65D8:iCloudProject
Using local storage: 0 for new NSFileManager current token <ef7a917f bca47ecf 5d58862d cbe9998d 7e53e5ea>

As you can see my fetch request executes before Using local storage: 0 and that's why I'm recieving records from the local storage (1 record) not iCloud (which contains more than 1).

I don't understand when to refetch my records. I take timer part from this great source for all who want to know more about Core Data and iCloud. But, 1 second isn't enought, 2 seconds is work as I want, but what if iCloud store will be bigger than mine, or network connection will be worse than mine, I don't think that timer is the way.

I hope somebody already face this trivial problem.

EDIT

I didn't find any help from Apple dev forum and SO, so I activated my code tech support token, I hope they can help me. As soon as I'll solve my problem I'll write an answer. But, if you, who read this question, know the possible answer, post it now.

like image 261
Dima Deplov Avatar asked Nov 09 '22 16:11

Dima Deplov


1 Answers

There is another notification NSPersistentStoreDidImportUbiquitousContentChangesNotification that is fired by the persistent store coordinator whenever data is imported from the ubiquitous content store. Here you can merge the changes with your NSManagedObjectContext.

When the ubiquity container receives changes from iCloud, Core Data posts an NSPersistentStoreDidImportUbiquitousContentChangesNotification notification. This notification’s userInfo dictionary is structured similarly to that of an NSManagedObjectContextDidSaveNotification notification except for that it contains NSManagedObjectID instances rather than NSManagedObject instances. Therefore you can merge in changes from other peers in the same way that you merge changes from other managed object contexts. Call mergeChangesFromContextDidSaveNotification: on your managed object context, passing in the notification object posted by Core Data.

https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/UsingCoreDataWithiCloudPG/UsingSQLiteStoragewithiCloud/UsingSQLiteStoragewithiCloud.html

like image 118
Ahmed Onawale Avatar answered Nov 15 '22 04:11

Ahmed Onawale