简体   繁体   中英

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?

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;
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM