Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enable / disable iCloud sync of Core Data while app is running

How can I add an option to enable and disable iCloud sync for Core Data (iOS7)?

Here are my thoughts / attempts:

To disable iCloud sync:

NSDictionary *options = @{NSPersistentStoreUbiquitousContentNameKey: @"MYStore"};
[NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:storeUrl options:options error:&error];

However, I think this may delete all the data from iCloud? I don't want that.

To enable iCloud sync:

NSDictionary *options = @{NSPersistentStoreUbiquitousContentNameKey: @"MYStore"};
[__persistentStoreCoordinator lock];
result = [__persistentStoreCoordinator migratePersistentStore:result toURL:storeUrl options:options withType:NSSQLiteStoreType error:&error];
[__persistentStoreCoordinator unlock];

In this code, I'm attempting to add options with the NSPersistentStoreUbiquitousContentNameKey key so that it starts syncing with iCloud. However, I don't want to move the store to a new location. Is this code proper?

How can I enable / disable iCloud while the app is running?

like image 662
Hope4You Avatar asked Nov 16 '13 19:11

Hope4You


People also ask

Does iCloud sync automatically?

Once you've set up your iCloud account and chosen the right storage plan for your needs, you'll want to enable iCloud backups for your iPhone and iPad. When it's enabled, your device will automatically back up data every 24-hours when it is plugged in to a power source and connected to wi-fi.

Do CloudKit apps sync with iCloud users?

This is perhaps the most obvious use for CloudKit: syncing your users' data across their devices. Example: a note taking app where the user can create and read notes on any device associated with their iCloud account. Alternatives: Realm Mobile Platform, Firebase, iCloud KVS, iCloud Documents, custom web app.

How does iCloud syncing work between iOS devices?

Syncing data between iOS devices has become a simple process thanks to iCloud. Many iOS apps use iCloud to store data. This makes their information easier to synchronize across your devices when you install the same app. You might wonder which of your apps use iCloud syncing. You can easily see the list on your device.

How to stop iCloud syncing completely?

Step 1. Unlock your iPhone and find the app Settings. Step 2. Go to Settings > [you name] > iCloud. You will find a list of categories here. Step 3. Turn off the options that you don't want to sync to iCloud. If you turn down all of them, you will stop iCloud syncing completely. Step 4.

How to synchronize data between iOS devices?

Syncing data between iOS devices has become a simple process thanks to iCloud. Many iOS apps use iCloud to store data. This makes their information easier to synchronize across your devices when you install the same app.

How do I use core data with iCloud?

To use Core Data with iCloud, you simply tell Core Data to create an iCloud-enabled persistent store. The iCloud service and Core Data take care of the rest: The system manages the files in the ubiquity container that make up your persistent store, and Core Data helps you keep your app up to date.


1 Answers

Below are the methods I use to enable or disable iCloud sync (OS X), essentially the store gets rebuilt each time, which is not the same as enabling and disabling iCloud sync while the app is running. So you wouldn't want to use this for "Pausing" synchronisation.

As I understand it you want something like the following:

If you turned off iCloud sync while the app is running and then perform some updates, you don't want those changes to be synchronised, but later when you turn iCloud sync back on you want subsequent changes to be synchronised.

I could be wrong but I think you might be out of luck using the standard Core Data/iCloud integration for this.

Depending on your requirements perhaps you could just observe the import notifications and discard any changes - but I can't see how you are not going to end up with all kinds of data inconsistencies that might cause subsequent imports to fail.

    // Removes the current Document from iCloud and deletes the local copy.
    // There does not appear to be any way of only removing ubiquitous content
    // and turning off iCloud sync.
    // So before we remove it we make a local copy by appending "_Backup" to the filename
    // (we should check this filename does not already exist and add a counter or something)
    //
    - (void)removeMeFromICloud {
        //LOG(@"removeMeFromICloud called");
        NSError *error;

        NSURL *currentURL = self.fileURL;
        NSString *fileType = self.fileType;
        NSString *extension = [currentURL pathExtension];
        NSString *path = [[currentURL URLByDeletingPathExtension] path];
        NSString *backupFilename = [NSString stringWithFormat:@"%@_Backup", path];
        NSURL *backupFileURL = [[[NSURL alloc] initFileURLWithPath:backupFilename] URLByAppendingPathExtension:extension];

        // We only have one store
        NSPersistentStore *currentStore = [self.managedObjectContext.persistentStoreCoordinator.persistentStores objectAtIndex:0];
        NSDictionary *currentOptions = currentStore.options;

        if ([self buildNewStoreAtURL:backupFileURL type:fileType error:&error]) {

            //FLOG(@" reset the moc...");
            [self.managedObjectContext reset];
            [self.managedObjectContext save:&error];

            //Set the document title
            //FLOG(@" current displayName is %@", self.displayName);
            self.fileURL = backupFileURL;

            //set the file extension hidden attribute to YES
            NSDictionary* fileAttrs = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
                                                                  forKey:NSFileExtensionHidden];
            if(![[NSFileManager defaultManager] setAttributes:fileAttrs
                                                 ofItemAtPath:[backupFileURL path]
                                                        error:&error])
                FLOG(@" unable to set file attributes to hide extension");

            // Now remove the old one
            bool success = [NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:currentURL options:currentOptions error:&error];

            if (success) {
                FLOG(@" removed document from iCloud");
                FLOG(@" using backup copy");
            }
            else  {
                FLOG(@" error removing document from iCloud");
                FLOG(@" error is %@, %@", error, error.userInfo);
            }
        } else {
            FLOG(@" error creating backup before removing from iCloud");
            FLOG(@" error is %@, %@", error, error.userInfo);
        }
    }

    // in order to migrate to the cloud we have to build the database from scratch,
    // we can't just open it with iCloud parameters because we have to ensure that
    // log files get generated in iCloud that will allow the store to be rebuilt from
    // scratch by peer devices.
    //
    - (void)migrateMeToICloud {
        //LOG(@"migrateMeToICloud called");

        NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;

        NSArray *stores = psc.persistentStores;
        NSError *error;

        // Now get current URL and add_iCloud to the document name, use this for the new store name
        NSURL *currentURL = self.fileURL;
        NSString *extension = [self.fileURL pathExtension];
        NSString *currentFilePathName = [[currentURL URLByDeletingPathExtension] path];
        NSString *uuidString = [[NSUUID UUID] UUIDString];
        NSString *newFilePathName = [NSString stringWithFormat:@"%@_UUID_%@",currentFilePathName, uuidString];

        // We must make it NSSQLite so get the right extension...
        NSURL *newURL = [[[NSURL alloc] initFileURLWithPath:newFilePathName] URLByAppendingPathExtension:extension];

        NSDictionary *options = [self storeOptionsForICloud:self.fileURL];
        NSString *ubiquityName = [options valueForKey:NSPersistentStoreUbiquitousContentNameKey];


        if ([stores count]) {
            NSPersistentStore *store = [stores objectAtIndex:0];

            if (store) {
                //FLOG(@" starting migration...");

                NSPersistentStore *newStore = [psc migratePersistentStore:store toURL:newURL options:options withType:NSSQLiteStoreType error:&error];
                //FLOG(@"  psc is %@", psc);

                //FLOG(@" done migrating...");
                if (newStore != nil) {
                    //FLOG(@" new store seems OK...");

                    // Set custom metadata so we know if it is synced in iCloud next time we open it
                    [self setiCloudMetaDataForStore:currentURL ofType:NSSQLiteStoreType iCloud:YES ubiquityName:ubiquityName];

                }
                else  {
                    FLOG(@" error migrating store");
                    FLOG(@" error is %@, %@", error, error.userInfo);

                }
            } else  {
                FLOG(@" store is nil, nothing to migrate!");
            }
        }

    }

// File is NEVER iCloud enabled when we do this.
// Is we do Save As we pnly build a local store wihout iCloud sync.
// User can select iCloud sync once Save As is done
//
- (bool)buildNewStoreAtURL:(NSURL*)newURL type:(NSString *)typeName error:(NSError **)error {

    //FLOG(@"buildNewStoreAtURL:type: called");

    NSError *myError;

    // We only have one store
    NSPersistentStore *currentStore = [self.managedObjectContext.persistentStoreCoordinator.persistentStores objectAtIndex:0];
    NSDictionary *currentOptions = currentStore.options;

    // We usually would need to create a new UUID for the new document if it is in iCloud.
    // But since we create a local copy only we don't need this.
    NSMutableDictionary *newOptions = [[NSMutableDictionary alloc] initWithDictionary:currentOptions];
    [newOptions setObject:@"DELETE" forKey:@"JOURNAL"];

    // Remove any iCloud options (this one includes the unique iCloud UUID
    [newOptions removeObjectForKey:NSPersistentStoreUbiquitousContentNameKey];

    // Remove Core Data ubiquity metadata
    [newOptions setObject:[NSNumber numberWithBool:YES] forKey:NSPersistentStoreRemoveUbiquitousMetadataOption];


    NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;

    // Now migrate the store to the new location
    NSPersistentStore *newStore = [psc migratePersistentStore:currentStore toURL:newURL options:newOptions withType:typeName error:&myError];

    if (newStore) {

        // Now set up our custom metadata so we can determine if it has been synced in iCloud next time we open it
        NSDictionary *dict = [self getiCloudMetaDataForStore:[psc metadataForPersistentStore:newStore] iCloud:NO ubiquityName:nil];

        [psc setMetadata:dict forPersistentStore:newStore];

        return YES;
    }
    else {

        FLOG(@" problem creating new document");
        FLOG(@"  - error is %@, %@", myError, myError.userInfo);
        *error = myError;
        return NO;
    }

}
like image 176
Duncan Groenewald Avatar answered Oct 14 '22 19:10

Duncan Groenewald