将 iCloud 商店迁移到本地

Posted

技术标签:

【中文标题】将 iCloud 商店迁移到本地【英文标题】:Migrating iCloud store to local 【发布时间】:2014-02-12 15:03:54 【问题描述】:

迁移在模拟器上运行良好。但是在设备上,我没有看到任何错误消息,但迁移的存储是空的。

NSDictionary *iCloudOptions = @
    NSPersistentStoreUbiquitousContentNameKey : @"iCloudNimbleStore",
    NSPersistentStoreUbiquitousContentURLKey : @"transactions_logs",
    NSMigratePersistentStoresAutomaticallyOption : @YES,
    NSInferMappingModelAutomaticallyOption : @YES
;
NSDictionary *localOptions = @NSMigratePersistentStoresAutomaticallyOption : @YES,
    NSInferMappingModelAutomaticallyOption : @YES
;
if (![[NSFileManager defaultManager]fileExistsAtPath:self.storeURL.path]) 
    @synchronized(@"Migration")
    
        // thread-safe code
        if ([[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]) 
            NSLog(@"iCloud");
            [self migrateStoreFromURL:[self nb_URLToStoreWithFilename:[self nb_appName]]options:iCloudOptions];
        else
            [self migrateStoreFromURL:[self nb_URLToStoreWithFilename:[NSString stringWithFormat:@"%@.sqlite", [self nb_appName]]] options:localOptions];
            //
            [self migrateStoreFromURL:[self nb_URLToOldStoreWithFilename] options:localOptions];
        
    


NSDictionary *options = @
    NSMigratePersistentStoresAutomaticallyOption:@YES
    ,NSInferMappingModelAutomaticallyOption:@YES
;
NSError *error = nil;
[_coordinator lock];
_store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:[self storeURL] options:options error:&error];
[_coordinator unlock];
if (!_store) 
    UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Loading Fail" message:[NSString stringWithFormat:@"Failed to add store. Error: %@", error] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert show];
    NSLog(@"Failed to add store. Error: %@", error);abort();
 else 
    UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Loading Success" message:[NSString stringWithFormat:@"Successfully added store: %@", _store] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    [alert show];
    NSLog(@"Successfully added store: %@", _store);
    if (_store && !error) 
        // Encrypt the password database
        NSError *encrError;
        NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
        if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:self.storeURL.path error:&encrError])
            NSLog(@"Unresolved error with password store encryption %@, %@", encrError, [encrError userInfo]); 
            abort();
        else NSLog(@"Encrypted");
    

这是迁移过程:

- (void)migrateStoreFromURL:(NSURL *)oldStoreURL options:(NSDictionary *)oldOptions
if (debug==1) 
    TFLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));

if (_store)

    NSLog(@"NOT NEEDED");
    return;


UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Migration" message:[NSString stringWithFormat:@"Found old store at %@",oldStoreURL.path] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alert show];

NSFileManager *fileManager = [NSFileManager defaultManager];

if (![fileManager fileExistsAtPath:self.storeURL.path]) 
    NSDictionary *options =
    @
      NSMigratePersistentStoresAutomaticallyOption:@YES
      ,NSInferMappingModelAutomaticallyOption:@YES
      ;
    NSError *error = nil;
    [_coordinator lock];
    NSPersistentStore *srcPS = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                                  configuration:nil
                                                                                            URL:oldStoreURL
                                                                                        options:oldOptions
                                                                                          error:&error];

    _store = [_coordinator migratePersistentStore:srcPS
                                                                             toURL:self.storeURL
                                                                           options:options
                                                                          withType:NSSQLiteStoreType
                                                                             error:&error];
    [_coordinator unlock];
    if (_store && !error) 
        UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Migration Success" message:[NSString stringWithFormat:@"Old store successfully migrated from %@",oldStoreURL.path] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];
        // Encrypt the password database
        NSError *encrError;
        NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:NSFileProtectionComplete forKey:NSFileProtectionKey];
        if (![[NSFileManager defaultManager] setAttributes:fileAttributes ofItemAtPath:self.storeURL.path error:&encrError])
            UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Encryption Error" message:[NSString stringWithFormat:@"Unresolved error with password store encryption %@, %@", encrError, [encrError userInfo]] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
            [alert show];
        

    else
        UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Migration Error" message:error.localizedDescription delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [alert show];
    


更新:我检查了新迁移的商店的大小,它是0。最奇怪的是_store && !error是真的。我还尝试将NSPersistentStoreRemoveUbiquitousMetadataOption: @YES 添加到迁移选项中,但它并没有改变任何东西。

更新。 2 我认为设备上的 iCloud 商店网址在加载之前为零。我需要一些解决方法才能等到它完成。

【问题讨论】:

您等待迁移完成的时间是否足够长?打开 Core Data 日志记录并监控 Xcode 中的 iCloud 网络流量,看看发生了什么。 不,我不等。问题是在设备上迁移后我得到了空商店。在启用了 iCloud 的模拟器上一切顺利。从 TestFlight 安装更新时,我无法使用日志记录。 【参考方案1】:

我不是 100% 确定我了解您要对迁移做什么。通过迁移在空存储中播种数据是很常见的,但看起来您正在尝试将数据从 iCloud 迁移到本地存储中。是对的吗?你不应该这样做。 iCloud 应该会自动将来自其他设备的数据添加到您的存储中。

这条线也不对:

NSPersistentStoreUbiquitousContentURLKey : @"transactions_logs",

我认为您想在那里使用指向 iCloud 容器内的事务日志目录的 URL。例如。

NSURL *containerURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSURL *url = [containerURL URLByAppendingPathComponent:@"transactions_logs"];

使用 iCloud 时,重要的是要意识到数据不会立即传输。可能需要一段时间才能到达,并且您的应用程序实际上无法确定是否有数据到来。您可以使用元数据查询来监控元数据,但即便如此,通常也会在其他设备上的数据已经生成之后的一段时间内到达。

因此,简单地在无处不在的容器中查找数据并没有多大帮助,因为可能有数据,也可能没有数据。你只是不知道,你必须在考虑到这个假设的情况下开发你的方法,以便它可以处理任何延迟。

使 iCloud 同步与 Core Data 一起工作所需的迁移是混乱且不必要的。您可能更有可能使用自动执行该操作的框架(例如 Core Data Ensembles)使事情正常运行。 (披露:我是 Ensembles 的开发者。)

【讨论】:

我试图用你的行替换内容 url,但它没有帮助。由于 ios7 这条线是完全有效的(它是在 WWDC2013、Core Data 和 iCloud 讲座中介绍的)。我使用了 Nimble 框架,该框架是专门为使用 iOS7 iCloud 同步优势而编写的,但是它在数据模型版本控制上出现问题,所以我决定将商店迁移到本地。迁移工作正常,在 Xcode 中测试时,但当我在 iPad 上测试时,它会创建 0 大小的文件而没有任何错误... 您确定迁移的文件实际上是空的吗?你是如何测试的?您是在查看存储文件大小,还是从存储中获取对象?我问的原因是,使用新的日志功能,如果缓冲的数据没有达到某个级别,sqlite 存储可以保持为空。 是的,我使用 AlertView 进行了测试,通过 TestFlight 安装时它为零。就在一分钟前,我决定检查该应用程序的先前版本,当我通过 TestFlight 发送它时,它的 store 也是 nil。所以这次迁移似乎没什么问题,但 TestFlight 更新开始删除文档数据。我前段时间更改了配置文件,也许这就是原因。

以上是关于将 iCloud 商店迁移到本地的主要内容,如果未能解决你的问题,请参考以下文章

将 PersistentStoreCoordinator 从本地迁移到 iCloud 时数据重复

在迁移时从 iCloud 迁移到本地商店崩溃的应用程序“对象不能为零” - 使用 Core Data

在 iOS 7 中禁用 Core Data 中的 iCloud(也就是将持久存储文件迁移到本地)

将 Core Data 应用程序迁移到 iCloud

iCloud 和轻量级迁移

iOS:将现有核心数据数据库迁移到 iCloud