将iCloud数据迁移到本地存储,并使iCloud停止响应

时间:2014-08-07 15:46:17

标签: ios objective-c core-data icloud

我非常接近在我的应用中完成iCloud和Core Data的实施。这是一款仅限iOS 7的应用。

我正在为用户提供应用程序中的选项,以UISwitch的形式说明使用iCloud On或Off。该应用程序启动并询问用户是否要使用iCloud,然后可以在应用程序中更改。

我将数据从本地存储(如果存在)迁移到iCloud。我挣扎的一个方面是将数据从iCloud Store迁移到本地商店。

方案: - 用户A有一个iPhone与我的应用程序和一些数据 - 用户A将应用程序下载到iPad上,选择使用iCloud,但稍后再确定他们不想在iPad上进行同步。因此,他们关闭了应用程序内的iPad iCloud同步。

此时,我的理解是,用户会希望数据仍然存在,但是从幕后,它现在是" LOCAL"而不是" iCloud"基于数据,这表示来自其他设备的条目不会同步到此设备,反之亦然

所以我正努力实现这一目标。

问题

我可以将数据从iCloud迁移到本地商店,但问题是因为iCloud仍然在设备上启用,下次运行应用程序时,我可以看到iCloud在应用程序中被禁用但是控制台仍然显示使用本地存储1然后0,并且来自iPhone的任何数据都会同步到iPad,即使使用iCloud UISwitch非常明显关闭。

这是执行迁移的主要代码:

- (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 to Local Migration之前的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 for显示:

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似乎没有任何效果。

我真的很感激对此的一些看法。

更新:此解决方案已被发现为以下两个答案的组合。对于关注此问题的任何人,请参阅此问题:Migrating an iCloud Store to a Local Store and making sure the data is there through each app launch

2 个答案:

答案 0 :(得分:1)

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

NSUbiquitousKeyValueStore *store = [NSUbiquitousKeyValueStore defaultStore];
[[NSNotificationCenter defaultCenter] addObserver:self
                                     selector:@selector(updateKVStoreItems:)
                                         name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
                                       object:store];

当您切换到本地数据时,您应该删除观察者,以便当Apple端的iCloud数据存储中的某些数据发生更改时,您将不再收到该设备的通知(如果他们切换,请务必添加观察者)回到iCloud)。

[[NSNotificationCenter defaultCenter] removeObserver:self name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification object:nil];

请注意,如果您将iCloud与Core Data商店一起使用,而不仅仅是像我正在展示的键值存储,那么您的通知可能会有所不同。

答案 1 :(得分:1)

这是一个两步过程:像@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类上调用singleton方法(removeUbiquitousContentAndPersistentStoreAtURL)。抓住有问题的iCloud商店文件(我将其称为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存储,并且应用程序已在本地存储上快乐地运行,请执行此操作。

从UI的角度来看,让所有设备上的所有数据迁移到本地商店后,让用户做出这个选择可能是一个好主意,而不是将其集成到"关闭iCloud"例行程序 - 只是因为处理那些仍然期望iCloud商店存在的同行可能会很棘手。