Ios 将iCloud数据迁移到本地存储并停止iCloud继续响应

Ios 将iCloud数据迁移到本地存储并停止iCloud继续响应,ios,objective-c,core-data,icloud,Ios,Objective C,Core Data,Icloud,我很快就要在我的应用程序中完成iCloud和核心数据的实现了。这是一款仅限iOS 7的应用程序 我在应用程序中为用户提供了一个选项,可以通过ui开关来打开或关闭iCloud。应用程序启动并询问用户是否要使用iCloud,然后可以在应用程序中更改 我将数据从本地存储(如果存在)迁移到iCloud。我正在努力解决的一个问题是将数据从iCloud存储迁移到本地存储 情景: -用户A有一部带有我的应用程序和一些数据的iPhone -用户A将应用程序下载到iPad上,选择使用iCloud,但后来决定不想在

我很快就要在我的应用程序中完成iCloud和核心数据的实现了。这是一款仅限iOS 7的应用程序

我在应用程序中为用户提供了一个选项,可以通过
ui开关
来打开或关闭iCloud。应用程序启动并询问用户是否要使用iCloud,然后可以在应用程序中更改

我将数据从本地存储(如果存在)迁移到iCloud。我正在努力解决的一个问题是将数据从iCloud存储迁移到本地存储

情景: -用户A有一部带有我的应用程序和一些数据的iPhone -用户A将应用程序下载到iPad上,选择使用iCloud,但后来决定不想在iPad上同步。因此,他们关闭了应用程序内的iPad iCloud同步

在这一点上,我的理解是,用户希望数据仍然存在,但在幕后,它现在是“本地”数据,而不是基于“iCloud”的数据,这意味着,来自其他设备的条目不会同步到此设备,反之亦然

所以我正在努力做到这一点

问题

我可以将数据从iCloud迁移到本地存储,但问题是,由于iCloud仍在设备上启用,因此下次运行应用程序时,我可以看到iCloud在应用程序中被禁用,但控制台仍然显示使用本地存储1,然后是0,iPhone的任何数据都会同步到iPad,即使使用iCloud
UI开关
已明显关闭

以下是执行迁移的主要代码:

- (void)migrateiCloudStoreToLocalStore {

    NSLog(@"Migrate iCloudToLocalStore");
    NSPersistentStore *store = self.persistentStoreCoordinator.persistentStores.lastObject;

    //NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Envylope.sqlite"];
    NSURL *storeURL = [self.persistentStoreCoordinator.persistentStores.lastObject URL];
    NSLog(@"Current Store URL (before iCloud to Local migration): %@", [storeURL description]);

    NSDictionary *localStoreOptions = nil;
    localStoreOptions = @{ NSPersistentStoreRemoveUbiquitousMetadataOption : @YES,
                           NSMigratePersistentStoresAutomaticallyOption : @YES,
                           NSInferMappingModelAutomaticallyOption : @YES};

    NSPersistentStore *newStore = [self.persistentStoreCoordinator migratePersistentStore:store
                                                                                     toURL:storeURL
                                                                                   options:localStoreOptions
                                                                                  withType:NSSQLiteStoreType error:nil];

    [self reloadStore:newStore];
}

- (void)reloadStore:(NSPersistentStore *)store {
    NSLog(@"Reload Store");        
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Envylope.sqlite"];
    NSDictionary *localStoreOptions = nil;
    localStoreOptions = @{ NSPersistentStoreRemoveUbiquitousMetadataOption : @YES,
                           NSMigratePersistentStoresAutomaticallyOption : @YES,
                           NSInferMappingModelAutomaticallyOption : @YES};

    if (store) {
        [self.persistentStoreCoordinator removePersistentStore:store error:nil];
    }

    [self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                  configuration:nil
                                                            URL:storeURL
                                                        options:localStoreOptions
                                                          error:nil];
    storeURL = [self.persistentStoreCoordinator.persistentStores.lastObject URL];
    NSLog(@"Current Store URL (after iCloud to Local migration): %@", [storeURL description]);

    NSLog(@"Done reloading");

    [[NSUserDefaults standardUserDefaults]setBool:YES forKey:@"MigratedFromiCloudToLocal"];
    [[NSUserDefaults standardUserDefaults]synchronize];

}
当然,其中有一些冗余代码,但iCloud到本地迁移之前的
NSLog
显示:

 Current Store URL (before iCloud to Local migration): file:///var/mobile/Applications/62CAFCEE-450E-410D-9D56-4918DD70EC07/Documents/CoreDataUbiquitySupport/mobile~98457667-A467-4095-BB7F-EF36EB2B0FE1/EnvyCloud/CD08912E-C135-4056-9557-6B47C84ECEF9/store/Envylope.sqlite
数据迁移后的NSLog显示:

Current Store URL (after iCloud to Local migration): file:///var/mobile/Applications/62CAFCEE-450E-410D-9D56-4918DD70EC07/Documents/Envylope.sqlite
这清楚地表明数据已迁移到本地存储。然而,在控制台中的这一点上,我不确定会发生什么,但理想情况下,这个过程应该与设备上根本没有启用iCloud具有相同的效果(如果我删除我的应用程序,删除设备上的iCloud帐户并运行应用程序,我会得到预期的效果-在没有任何iCloud选项的情况下运行应用程序)。这并没有发生,因为它继续推进iCloud更新。在不再次运行应用程序的情况下,其他设备中添加的数据将在此处同步。再次运行应用程序时,控制台中会显示使用本地存储1和0

因此,数据可能已迁移到本地存储,但在应用程序中禁用iCloud似乎没有任何效果

我真的很想听听你的想法


更新:解决方案是以下两个答案的组合。如果您想了解下一个场景,请参考此问题:

您似乎正在正确迁移数据存储,但仍然会收到iCloud修改的通知。在代码中的某个时刻,您正在注册iCloud更改的观察者,例如

NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
[[NSNotificationCenter defaultCenter] addObserver:self
                                     selector:@selector(updateKVStoreItems:)
                                         name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
                                       object:store];
当您切换到本地数据时,您应该删除观察者,这样当苹果端的iCloud数据存储中的某些数据发生更改时,您就不会再在该设备上收到通知(如果他们切换回iCloud,请确保重新添加观察者)


请注意,如果您将iCloud与核心数据存储一起使用,而不是像我所展示的那样只是一个键值存储,那么您的通知可能会有所不同。

这是一个两步过程:如@wottle所建议的,从您的应用程序中删除所有三个观察者。将iCloud数据迁移到新的本地存储文件后执行此操作:

- (void)removeCloudObservers {

[[NSNotificationCenter defaultCenter]removeObserver:self name:NSPersistentStoreCoordinatorStoresWillChangeNotification object:self.managedObjectContext.persistentStoreCoordinator];
[[NSNotificationCenter defaultCenter]removeObserver:self name:NSPersistentStoreCoordinatorStoresDidChangeNotification object:self.managedObjectContext.persistentStoreCoordinator];
[[NSNotificationCenter defaultCenter]removeObserver:self name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:self.managedObjectContext.persistentStoreCoordinator];
}
接下来,如果您完全确定要从iCloud中删除整个泛在容器(正如我们之前手动所做的那样),则可以调用NSPersistentStoreCoordinator类(RemoveUbiquitousContentandPersistentStoreAtural)上的singleton方法。抓取有问题的iCloud store文件(我将其称为yourStore),然后使用如下内容:

- (void)removeCloudContainer:(NSPersistentStore *)yourStore {

NSError *error = nil;
if (![NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:yourStore.URL options:yourStore.options error:&error]) {
    NSLog(@"That didn't work so well, and here's why: %@", error.localizedFailureReason);
}
}
这将移除整个容器,其他设备将无法再访问iCloud数据。一旦iCloud应用商店从coordinator中移除,并且应用程序已经在本地应用商店中愉快地运行时,请执行此操作


从用户界面的角度来看,让用户在所有设备上的所有数据都迁移到本地存储后做出选择,而不是将其集成到“关闭iCloud”例程中,这可能是一个好主意,只是因为与仍然希望有iCloud存储的对等方打交道可能会比较棘手

非常感谢你的回复,杰。那真是太完美了。我想基本上只关闭与对等服务器的iCloud同步,这样我就不会使用那里的辅助代码来完全删除所有iCloud数据。我基本上希望用户能够关闭一个设备的iCloud,只要这是他们想要的。当然,他们也可以关闭第二台设备。删除通知真的很有帮助-非常感谢。然而,我确实有另一种情况发生在那之后…因此用户关闭了iCloud同步。。数据仍然在应用程序中(这很好),但当再次加载应用程序时,它会删除所有我不想要的数据。我希望数据在那里。因此,它正在成功地从iCloud迁移到本地,但当应用程序再次运行时,它不会运行该代码。我将更新我的问题,以反映persistentStoreCoordinator,这是一切发生的地方。为了清楚起见,我在这里问了一个问题:-希望它有意义!非常感谢你的回答。删除通知真的很有帮助,现在我正面临我希望的最后一个问题。我刚刚更新了我的问题。通常,你应该创建一个新问题,以便有类似问题的人可以找到解决方案。两个部分的问题在最初的答案后会发生变化,这对其他人来说以后很难理解
- (void)removeCloudContainer:(NSPersistentStore *)yourStore {

NSError *error = nil;
if (![NSPersistentStoreCoordinator removeUbiquitousContentAndPersistentStoreAtURL:yourStore.URL options:yourStore.options error:&error]) {
    NSLog(@"That didn't work so well, and here's why: %@", error.localizedFailureReason);
}
}