在应用程序运行时启用/禁用Core Data的iCloud同步

时间:2013-11-16 19:28:15

标签: ios objective-c core-data ios7 icloud

如何为Core Data(iOS7)添加启用和禁用iCloud同步的选项?

以下是我的想法/尝试:

禁用iCloud同步:

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

但是,我认为这可能会删除iCloud中的所有数据?我不希望这样。

启用iCloud同步:

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

在此代码中,我尝试使用options键添加NSPersistentStoreUbiquitousContentNameKey,以便它开始与iCloud同步。但是,我不想将商店移到新的位置。这段代码是否合适?

如何在应用运行时启用/禁用iCloud?

1 个答案:

答案 0 :(得分:2)

以下是我用来启用或禁用iCloud同步(OS X)的方法,实际上每次都会重建商店,这与在应用程序运行时启用和禁用iCloud同步不同。因此,您不希望将此用于“暂停”同步。

据我了解,您需要以下内容:

如果您在应用程序运行时关闭了iCloud同步,然后执行了一些更新,则不希望这些更改同步,但稍后当您重新启用iCloud同步时,您希望后续更改同步。

我可能错了,但我认为使用标准Core Data / iCloud集成可能会运气不好。

根据您的要求,您可能只是观察导入通知并放弃任何更改 - 但我看不出您最终会如何处理可能导致后续导入失败的各种数据不一致。

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

}