Core Data with iCloud Notifications in iOS 7

core-data, icloud, ios7, objective-c

Solution

I can't find a will version of this notification that I can use to show loader for example that the database will be updated

Yeah, that's because there isn't one. When new changes come in, the iCloud daemon saves them and then tells you what it has done.

Since I can't find a will version, then I used the same notification to display a loader to tell the user that the database is being updated ... The problem in this notification is that it can be called several time if the content changed in iCloud is relatively large which lead me to display several loading indicator !

Yes! I had this same issue. These notifications can be very frequent, so you don't want to notify the user every time one comes in. What I did was arrange to delay the notification until iCloud had quieted down for at least 1 second. You can do this using an `NSTimer`. First declare it as a property:

@property (readwrite, retain) NSTimer *iCloudUpdateTimer;

Then when you get the "did import" notification, do something like this:

if (self.iCloudUpdateTimer == nil) {
    self.iCloudUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
        target:self
        selector:@selector(notifyUser:)
        userInfo:nil
        repeats:NO];
} else {
    [self.iCloudUpdateTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:1.0];
}

// Now actually handle the notification....

What this does: Creates the timer if it doesn't exist. If it does exist, it delays the fire date for one second. When iCloud sends a lot of notifications, the timer keeps getting pushed out into the future. The timer will finally fire one second after iCloud calms down.

Then when the timer fires, do something like:

- (void)notifyUser:(NSNotification *)notification
{
    [self.iCloudUpdateTimer invalidate];
    self.iCloudUpdateTimer = nil;

    // Then notify the user
}

You may need to use `dispatch_async` to make sure that all timer access happens on the same queue.

(As an aside, even if there were a "will import" notification, it would probably be posted just as often as the current "did import" notification, so it wouldn't help you very much.)

Is there a way that I can use to trigger the update through iCloud manually?

No, you have absolutely no control over that.

Problem

I have added iCloud support to my app. when the user update his data I got a notification `NSPersistentStoreDidImportUbiquitousContentChangesNotification` That's great However, I have these problems - I can't find a `will` version of this notification that I can use to show loader for example that the database will be updated - Since I can't find a `will` version, then I used the same notification to display a loader to tell the user that the database is being updated ... The problem in this notification is that it can be called several time if the content changed in iCloud is relatively large which lead me to display several loading indicator ! - Is there a way that I can use to trigger the update through iCloud manually? . I added iCloud support to Core data by doing this: in NSManagedObjectContext ``` _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; _managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; ``` in NSPersistentStoreCoordinator ``` [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:@{NSPersistentStoreUbiquitousContentNameKey:@"iCloudStore"} error:nil]; ``` and In `NSPersistentStoreDidImportUbiquitousContentChangesNotification` notification method ``` - (void)persistentStoreDidImportUbiquitousContentChanges:(NSNotification *)notification { NSManagedObjectContext *managedObjectContext = self.managedObjectContext; [managedObjectContext performBlock:^{ [managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; dispatch_async(dispatch_get_main_queue(), ^{ UIWindow *mainWindow = [[[UIApplication sharedApplication] delegate] window]; MBProgressHUD *hud = [MBProgressHUD HUDForView:mainWindow]; if (hud == nil) { hud = [[MBProgressHUD alloc] initWithWindow:mainWindow]; } [mainWindow addSubview:hud]; hud.labelText = NSLocalizedString(@"Progress updated", nil); [hud show:YES]; [hud hide:YES afterDelay:1]; // Post Notification [[NSNotificationCenter defaultCenter] postNotificationName:RESET_APPLICATION_NOTIFICATION object:nil]; }); }]; } ```

Original source