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

Posted

技术标签:

【中文标题】在应用程序运行时启用/禁用 Core Data 的 iCloud 同步【英文标题】:Enable / disable iCloud sync of Core Data while app is running 【发布时间】:2013-11-16 19:28:14 【问题描述】:

如何添加选项以启用和禁用 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];

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

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

【问题讨论】:

您好,您找到任何解决方案或解决您的要求了吗?我对我的应用程序有相同的要求。如果您有任何解决方案,请分享。谢谢。 【参考方案1】:

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

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

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

p>

我可能是错的,但我认为使用标准的 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;
    


【讨论】:

谢谢,但是这似乎没有实现新的 iOS7 方法 iCloud(即设置NSPersistentStoreUbiquitousContentNameKey)。我将使用新的 iOS7 方法。

以上是关于在应用程序运行时启用/禁用 Core Data 的 iCloud 同步的主要内容,如果未能解决你的问题,请参考以下文章

如何在运行时启用/禁用“org.eclipse.ui.editors”扩展

如何在发布更新时启用 iCloud 并迁移 Core Data?

在运行时启用/禁用 SwaggerUI

在 iOS 应用程序运行时启用/禁用 Crashlytics

在运行时启用和禁用 gprof?

如何在应用程序处于后台时执行 Core Data 操作