【问题标题】:Crash when adding persistent store (iCloud enabled) in app delegate在应用程序委托中添加持久存储(启用 iCloud)时崩溃
【发布时间】:2011-12-22 17:35:48
【问题描述】:

我将开始更新此内容,以帮助那些希望将其用作个人代码参考的人。

最新更新

  • 我很确定我找到了一种方法,可以在设备停止相互通信后重新同步设备。我要更新我的 在下面回答所有细节。我非常希望你们都能找到 这很有帮助。花了将近 2 个月的反复试验才弄清楚 这一出。所以请参考并与其他人分享 有类似的问题让设备再次与每个设备通话 其他通过 iCloud。我花了很长时间才弄清楚这一切,所以我 我非常乐意尽可能多地拯救其他开发人员 必须创建自己的临时修复程序。

另一个有助于正确设置的附加功能

  • 我发现在更新关联了 iCloud 数据的应用程序后 使用该帐户可能会在打开它时导致崩溃,因为 iCloud 数据将尝试立即合并到设备中(其中 设备尚未设置其持久存储)。我现在添加了 @property (nonatomic, readwrite) BOOL unlocked;AppDelegate.h@synthesize unlocked;AppDelegate.m。然后我将我的- (NSPersistentStoreCoordinator *)persistentStoreCoordinator 方法更改为 以及我的- (void)mergeChangesFrom_iCloud 方法,这两者 将在下面显示(在持久存储设置的中间和 在 iCloud 合并方法的底部)。本质上,我在说 应用程序以防止 iCloud 在应用程序设置之前合并数据 它的持久存储。否则,您将看到应用程序由于以下原因而崩溃 无法读取的故障。

这是我设置persistentStoreCoordinator的方式:

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (__persistentStoreCoordinator != nil)
    {
        return __persistentStoreCoordinator;
    }


    // here is where you declare the persistent store is not prepared;
    self.unlocked = NO;

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Maintain_My_Car.sqlite"];

    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];   

    NSPersistentStoreCoordinator *psc = __persistentStoreCoordinator; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSDictionary *options = nil;

        NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];

        NSString *coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"data"];

        if (coreDataCloudContent.length != 0) {
            // iCloud enabled;

            cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];
            options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, @"<bundleIdentifier>.store", NSPersistentStoreUbiquitousContentNameKey, cloudURL, NSPersistentStoreUbiquitousContentURLKey, nil];

        } else {

            // iCloud not enabled;
            options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

        }

        NSError *error = nil;

        [psc lock];

        if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {

            NSLog(@"bad things %@ %@", error, [error userInfo]);
            abort();

        }
        [psc unlock];

        // the store is now prepared and ready for iCloud to import data;
        self.unlocked = YES;


        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"iCloud persistent store added");

            [[NSNotificationCenter defaultCenter] postNotificationName:@"RefetchAllDatabaseData" object:self userInfo:nil];

        });
    });

    return __persistentStoreCoordinator;
}

&lt;myAppKey&gt;&lt;bundleIdentifier&gt; 当然是实际值。我只是为了共享此代码而掩盖它们。

我知道有些人对此仍有疑问,可能会将此问题用作有关如何设置自己的支持 iCloud 的 Core Data 应用程序的参考,因此我想在更改我的个人代码时更新此问题,确保你们所有人都可以使用适合我的代码。在本次更新中,我将初始 cloudURL 从 [fileManager URLForUbiquityContainerIdentifier:@"&lt;TeamIdentifier&gt;.&lt;bundleIdentifier&gt;"] 更改为 [fileManager URLForUbiquityContainerIdentifier:nil],确保从权利文件中收集容器信息。

其他方法 _notificationArray 定义如下: @property (nonatomice, strong) NSMutableArray *notificationArray; @synthesize notificationArray = _notificationArray;

- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {
    if (self.unlocked) {
        NSManagedObjectContext *moc = [self managedObjectContext];

        if (self.notificationArray.count != 0) {
            for (NSNotification *note in _notificationArray) {
                [moc performBlock:^{
                    [self mergeiCloudChanges:note forContext:moc];
                }];
            }
            [_notificationArray removeAllObjects];
            [moc performBlock:^{
                [self mergeiCloudChanges:notification forContext:moc];
            }];
        } else {
            [moc performBlock:^{
                [self mergeiCloudChanges:notification forContext:moc];
            }];
        }
    } else {
        if (_notificationArray == nil) {
            _notificationArray = [[NSMutableArray alloc] init];
        }
        [_notificationArray addObject:notification];
    }
}

- (void)resetStore {
    [self saveContext];
    __persistentStoreCoordinator = nil;
    __managedObjectContext = nil;
    // reset the managedObjectContext for your program as you would in application:didFinishLaunchingWithOptions:
    myMainView.managedObjectContext = [self managedObjectContext];
    // the example above will rebuild the MOC and PSC for you with the new parameters in mind;
}

然后是mergeiCloudChanges:forContext:方法:

- (void)mergeiCloudChanges:(NSNotification *)note forContext:(NSManagedObjectContext *)moc {
    // below are a few logs you can run to see what is being done and when;
    NSLog(@"insert %@", [[note userInfo] valueForKey:@"inserted"]);
    NSLog(@"delete %@", [[note userInfo] valueForKey:@"deleted"]);
    NSLog(@"update %@", [[note userInfo] valueForKey:@"updated"]);
    [moc mergeChangesFromContextDidSaveNotification:note];

    NSNotification *refreshNotification = [NSNotification notificationWithName:@"RefreshAllViews" object:self userInfo:[note userInfo]];
    [[NSNotificationCenter defaultCenter] postNotification:refreshNotification];
    // do any additional work here;
}

最初的问题

  • 在 iOS 5.0.1 上使用 iCloud,我偶尔会遇到与 到持久存储。我将继续更新这个 我通过实验发现的新信息,但到目前为止 我提供的解决方案是让应用程序正常工作的唯一方法 再次正确(不幸的是 jlstrecker 的解决方案不适用于 我)一旦我开始看到错误,如下所示:

    -NSPersistentStoreCoordinator addPersistentStoreWithType:configuration:URL:options:error:: CoreData:Ubiquity:尝试读取 ubiquity 根 url 时出错: 文件://localhost/private/var/mobile/Library/Mobile%20Documents/./data/。 错误:错误域=LibrarianErrorDomain Code=1“操作 无法完成。 (LibrarianErrorDomain 错误 1 ​​- 无法 启动项目下载。)" UserInfo=0x176000 {NSURL=file://localhost/private/var/mobile/Library/Mobile%20Documents/./data/, NSDescription=无法启动项目下载。}

    对于我的一生,我无法弄清楚为什么我会看到这一切 突然或如何让它停止。我已经从两者中删除了该应用程序 设备,删除了之前同步的 iCloud 数据 他们,并从有关应用程序的备份中删除了所有数据。我有 重新启动 Xcode,重新启动两个设备,清理 Xcode 项目, 然而,没有什么能阻止错误的出现。我从未见过 这个错误之前,并且在网上找到任何东西的运气为零 如何确定。

    应用程序在此处崩溃:

    if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
    
        NSLog(@"bad things %@ %@", error, [error userInfo]);
        abort();
    
    }
    

    永远不会命中日志,也不会中止。我只看到上面的错误 并且应用程序本身变得无响应。如果有人可以帮我指点 在正确的方向,我会非常感激。

以前的问题/问题

  • 即使从 beta 更新到 5.0.1 的公开发布。上一次发生在我身上是在 更改我的托管上下文数据模型。考虑到我没有 发布了应用程序,我没有费心合并新版本的 模型。我刚刚在我的设备上删除并重新安装了该应用程序,但是 然后它拒绝与存储在 iCloud 中的数据合作 容器,我的意思是我收到了商店的错误 无法下载项目。我想这是由于数据冲突 模型类型,这很有意义。所以看来你只需要 摆脱 iCloud 容器中的数据而不摆脱 的容器。删除 iCloud 数据似乎会杀死一切 off,本质上是禁用容器和 App ID。既然看起来 更简单,我尝试按照 jlstrecker 的建议创建一个新容器, 但不幸的是,这根本没有帮助。所以再一次,我不得不 完成我在回答中概述的步骤,这又做了 诡计。但是考虑到必须创建新应用程序是多么烦人 ID 和每次更新配置文件,我认为最好 更新我所学到的可能缩小原因并得到 更快的解决方案。

    通过 iCloud > 存储和备份 > 管理存储,然后 删除应用程序似乎是清空应用程序的最佳解决方案 数据,但这样做似乎会破坏容器,导致 上面的错误。而在成功做到这一点之后,不管有多少 有时我删除该应用程序并将其重新安装到设备上(使其 看起来像是第一次出现在设备上,希望 重新创建容器),我永远无法让应用程序显示在 文件和数据列表再次。这有点令人担忧,如果它意味着 任何像这样从 iCloud 中删除数据的人都意味着 iCloud 将不再适用于该应用程序。我只使用一个 到目前为止应用程序上的开发配置文件,所以也许使用 分发配置文件可能会有所不同,但我将不得不 在确定说任何话之前先测试一下。

我希望这些新更新可以帮助任何在设置商店时遇到困难的人。到目前为止,它对我来说效果很好。如果我找到更好的修复或任何使过程更加流畅的东西,我一定会更新更多。

【问题讨论】:

  • 我也可以像您一样通过在“管理存储”中删除我的应用程序数据来重现此错误。一个修复似乎是更改 NSPersistentStoreUbiquitousContentURLKey - 例如,我会将后缀从“/data”更改为“/data2”,它将再次开始工作。这当然不是解决方案。
  • slev,在我看来,它就像 iCloud 中的一个错误。报告——rdar://10440734
  • 我和你在一起,@jlstrecker。似乎 iCloud 容器从应用程序中删除了数据以及初始化自身所需的数据。我很高兴看到你报告了这个问题。我也应该这样做还是一个就足够了?
  • 另外,我似乎能够通过清除 iCloud 容器来“重置”文件夹:[fileManager removeItemAtURL:cloudUrl error:&error] 所以当 iCloud 刚刚获胜时,这可能是一个很好的“主重置”选项不同步。但是,当您执行此操作时,似乎数据库中的现有内容不会同步 - 只是新内容?
  • @pkclSoft 我会看看我能做些什么来为此目的在 github 上建立一个测试项目。我马上就要放几天假了,然后会继续努力。上传后,我将使用指向源代码的链接更新此线程

标签: iphone core-data ios5 icloud


【解决方案1】:

更新了重新同步您的设备的答案 几个月的修补让我弄清楚(我相信)根本问题是什么。问题在于让设备在不同步后再次相互通信。我不能确定是什么原因造成的,但我怀疑事务日志已损坏,或者(更有可能)日志本身的容器被重新创建。这就像设备 A 将更改发布到容器 A 和设备 B 一样,而不是同时发布到容器 C,它们可以读取/写入日志。

现在我们知道了问题所在,接下来就是创建解决方案了。更多的修补使我想到了以下内容。我有一个名为resetiCloudSync:(BOOL)isSource 的方法,它是我原来问题中上述方法的修改版本。

- (void)resetiCloudSync:(BOOL)isSource {
    NSLog(@"reset sync source %d", isSource);
    NSManagedObjectContext *moc = self.managedObjectContext;

    if (isSource) {
        // remove data from app's cloud account, then repopulate with copy of existing data;

        // find your log transaction container;
        NSURL *cloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
        NSString *coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"store"];
        cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];
        NSError *error = nil;

        // remove the old log transaction container and it's logs;
        [[NSFileManager defaultManager] removeItemAtURL:cloudURL error:&error];

        // rebuild the container to insert the "new" data into;
        if ([[NSFileManager defaultManager] createFileAtPath:coreDataCloudContent contents:nil attributes:nil]) {

            // this will differ for everyone else. here i set up an array that stores the core data objects that are to-many relationships; 
            NSArray *keyArray = [NSArray arrayWithObjects:@"addedFields", @"mileages", @"parts", @"repairEvents", nil];

            // create a request to temporarily store the objects you need to replicate;
            // my heirarchy starts with vehicles as parent entities with many attributes and relationships (both to-one and to-many);
            // as this format is a mix of just about everything, it works great for example purposes;
            NSFetchRequest *request = [[NSFetchRequest alloc] init];
            NSEntityDescription *entity = [NSEntityDescription entityForName:@"Vehicle" inManagedObjectContext:moc];
            [request setEntity:entity];
            NSError *error = nil;
            NSArray *vehicles = [moc executeFetchRequest:request error:&error];

            for (NSManagedObject *object in vehicles) {
                NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:object.entity.name inManagedObjectContext:moc];
                // check regular values;
                for (NSString *key in object.entity.attributesByName.allKeys) {
                    [newObject setValue:[object valueForKey:key] forKey:key];
                }

                // check relationships;
                NSMutableSet *relSet = [[NSMutableSet alloc] init];
                for (NSString *key in object.entity.relationshipsByName.allKeys) {
                    [relSet removeAllObjects];

                    // check to see relationship exists;
                    if ([object valueForKey:key] != nil) {

                        // check to see if relationship is to-many;
                        if ([keyArray containsObject:key]) {
                            for (NSManagedObject *toManyObject in [object valueForKey:key]) {
                                [relSet addObject:toManyObject];
                            }
                        } else {
                            [relSet addObject:[object valueForKey:key]];
                        }

                        // cycle through objects;
                        for (NSManagedObject *subObject in relSet) {
                            NSManagedObject *newSubObject = [NSEntityDescription insertNewObjectForEntityForName:subObject.entity.name inManagedObjectContext:moc];
                            // check sub values;
                            for (NSString *subKey in subObject.entity.attributesByName.allKeys) {
                                NSLog(@"subkey %@", subKey);
                                [newSubObject setValue:[subObject valueForKey:subKey] forKey:subKey];
                            }
                            // check sub relationships;
                            for (NSString *subRel in subObject.entity.relationshipsByName.allKeys) {
                                NSLog(@"sub relationship %@", subRel);
                                // set up any additional checks if necessary;
                                [newSubObject setValue:newObject forKey:subRel];
                            }
                        }
                    }
                }   
                [moc deleteObject:object];
            }
            [self resetStore];
        }
    } else {
        // here we remove all data from the current device to populate with data pushed to cloud from other device;
        for (NSManagedObject *object in moc.registeredObjects) {
            [moc deleteObject:object];
        }
    }
    [[[UIAlertView alloc] initWithTitle:@"Sync has been reset" message:nil delegate:nil cancelButtonTitle:@"Dismiss" otherButtonTitles:nil] show];
}

在这段代码中,我有两条不同的路径可供选择。一种是用于不同步且需要从源设备导入数据的设备。该路径所做的只是清除内存,以便为应该在其中的数据做好准备。

另一个 (isSource = YES) 路径做了很多事情。通常,它会删除损坏的容器。然后它创建一个新容器(让日志有一个地方驻留)。最后,它搜索父实体并复制它们。这样做是用应该存在的信息重新填充事务日志容器。然后您需要删除原始实体,以免重复。最后,重置持久化存储以“刷新”应用的核心数据并更新所有视图和fetchedResultsControllers

我可以证明这非常有效。我已经从设备 (isSource = NO) 中清除了数据,这些设备几个月没有与主设备(数据所在的位置)进行通信。然后我从主设备推送数据,并欣喜地看着我的所有数据在几秒钟内出现。

再次,请随时参考并分享给所有在与 iCloud 同步时遇到问题的人。

回答原始问题,iOS 5.1 发布后不再受影响,修复了在设置中删除应用的 iCloud 存储后崩溃

在尝试了很多很多小时来解决这个问题之后,我尝试创建一个新的 App ID,更新了应用程序的相关配置文件,更改了 iCloud 容器字段以匹配新的配置文件,然后一切都恢复正常了.我仍然不知道为什么会发生这种情况,但与该 App ID 关联的 iCloud 存储似乎已损坏?

所以最重要的是,如果其他人发生这种情况,请按照以下步骤操作,您应该会很好:

  1. 在 Provisioning Portal 中创建一个新的 App ID。
  2. 查找与应用关联的配置文件。点击 Edit->Modify,然后将 App ID 更改为您刚刚创建的那个。
  3. 提交更改,然后将 Xcode 中的现有配置文件替换为您刚刚创建的配置文件。
  4. 更改 &lt;bundleIdentifier&gt; 的所有实例以适应新的 App ID(这些将在您的主应用程序摘要页面、iCloud 容器和 iCloud 键值存储的权利以及您正在创建持久化的 AppDelegate 文件中)商店,就像我上面的代码一样)。
  5. 重新启动 Xcode,因为您更改了有关配置文件的信息(否则它会抱怨并拒绝在设备上运行)。
  6. 确保新配置文件位于您希望安装应用程序的设备上,然后构建并运行。此时一切都应该正常工作。

【讨论】:

  • 重要的问题是我们什么时候应该使用这个?当我们看到他们看起来不像是在互相攻击?还是经常?
  • 是的,这种resetiCloudSync:(BOOL)isSource 方法应该在设备似乎停止相互通信时使用。它基本上从容器中删除 iCloud 通知,然后用源数据库的整个内容填充容器。这可确保将所有数据发送到其他设备。在使用中,这种方法似乎几乎每次都能奏效,尽管我从一些客户那里听说他们的设备仍然不说话,所以我继续深入研究他们的个人问题。
  • 我真的很好奇为什么只有少数人(像你)会谈论它。它可能会在许多应用程序上引起许多问题!即使创建了新的应用程序 ID,它总有一天会再次发生。但是你做的很棒!
  • 只是想清楚地知道如何使用它。所以一旦设备似乎停止互相交谈,我们应该做的是运行“[delegate resetiCloudSync:YES];”在主设备上运行“[delegate resetiCloudSync:NO];”在子设备上?我说的对吗?
  • @BrightLee 我不知道为什么这么少的人解决这个问题。运气好的话,稍后会有一些可靠的方法可用,因为 Apple 示例中提供的股票代码并不总是有效。你对主设备和子设备是正确的。包含您希望推送的信息的主将发送[delegate resetiCloudSync:YES],而您希望接收信息的设备应该使用相同的,但使用NO
【解决方案2】:

另一个澄清:类似的情况发生在我在 iOS 6.0.1 和 6.1 beta 2 上测试设备。

正如@Slev 所述,它在 iOS 5.1 中并未完全修复。一台设备在尝试访问 iCloud 上的持久存储时会完全冻结大约 80 秒,但实际上永远不会访问存储在那里的信息。

我认为这是由于设备操作系统中的日志文件损坏所致。删除设备上的应用程序或 iCloud 数据并不能解决冻结/无法访问 iCloud 商店的问题。

我发现的一个修复是在设备上@9​​87654321@(也可以删除所有内容)。 settings-&gt;general-&gt;reset.

只有这样我才能再次使用该设备上的应用访问 iCloud 上的数据。我希望这可以帮助其他任何人来这里寻找一个非常令人沮丧的错误的解决方案。

【讨论】:

  • 感谢分享。知道使用“重置所有设置”选项将修复一些挥之不去的影响可能在尝试找到永久解决方案时非常有用
【解决方案3】:

我在 5.0.1 beta 1 上使用一台设备并在 5.0.0 上使用一台设备时遇到此错误。

我通过更改 iCloud 容器的名称消除了错误。在 iCloud Containers 列表中,第一个必须与您的应用 ID 匹配,但您可以添加具有不同名称的其他容器。

(和slev的改变app ID的方案一样,只有在app还没有发布的情况下,这才是一个不错的方案。)

【讨论】:

  • 删除 iCloud 数据时(系统偏好设置 -> iCloud -> 管理 -> 删除),出现错误。它说它无法删除数据(没有解释)。当我关闭并重新打开视图时,数据似乎已经消失了,但也许并没有全部消失。这可能是相关的。
  • 我不能说我试图通过我的电脑删除数据。我通过使用设置 > iCloud > 存储和备份 > 管理存储的设备完成了这一切。我这样做没有问题,尽管当我尝试使用新的 App ID 更新我的配置文件之前,这并没有为我解决任何问题(尽管使用新的 iCloud 容器名称也是一个好主意,并且更简单)。我现在正在下载/安装 5.0.1 的官方版本,因为它的行为就像是最新的无线更新一样。如果问题再次出现,我将报告错误
  • 我只是想指出,我完成了两台设备的更新,然后安装了我正在编写的应用程序的更新版本(这是我上次更新时这个问题毁了我的地方)并且我的问题为零.因此,如果它是操作系统中的错误,它似乎已修复。如果它与操作系统没有任何关系,那么我不知道它发生在哪里或为什么会发生。但我很想知道它在更新后是否也适合你
  • 我在 5.0.1 中看到了这个问题
  • @kurtzmarc 这里也一样,所以我用我看到的新事物和潜在的原因/解决方案更新了我的问题。到目前为止,我提供的解决方案是我能够用来成功让应用再次正常运行的唯一过程
【解决方案4】:

对不起,Slev,当您说应用程序崩溃时,我遇到了同样的问题,因为 iCloud 数据将尝试立即合并到设备中(设备尚未设置其持久存储)但我不明白你是怎么解决这个问题的。

你的代码是:

- (void)mergeChangesFrom_iCloud:(NSNotification *)notification {
    if (self.unlocked) {
       NSManagedObjectContext *moc = [self managedObjectContext];
        [moc performBlock:^{
            [self mergeiCloudChanges:notification forContext:moc];
        }];
    }
}

我还没有尝试过,但是看着这段代码,我想知道当“解锁”为假时到达的通知会发生什么。你会放开他们吗?

有一个while循环来测试“解锁”属性并花费一些时间跨度直到该属性变为真,这不是更好吗?

我希望你能理解我非常糟糕的英语...... :) 谢谢
戴夫

【讨论】:

  • 嗨,戴夫,这是一个很好的观点。我的假设是通知将继续发送,直到它们被系统识别(就像 iCloud 会尝试下载一个项目直到它成功一样)。设置一个标记来临时存储通知,直到永久存储被解锁,这将是一个很好的保障。我现在将尝试用它编写一个修改后的版本,并在它准备好时更新我的​​代码。感谢您的来信
  • 好的,代码更新了。如果 iCloud 不重新发送未合并到上下文中的信息,则此更新的方法将对此进行说明,存储任何可能丢失的通知,并将它们按出现的顺序合并回上下文中。我已经在我的手机上进行了测试,以确保它可以正常工作并且运行顺利
  • 哇,slev,我真的不知道通知是否会继续发送,直到它们被重新识别...我认为不会,所以这就是为什么我问什么时候到达的通知会发生什么“解锁”是错误的...... :) 但是,感谢您的代码解决方案!现在我的 iCloud 同步只有一些问题,我想我搞砸了所有 iCloud 数据,从首选项面板中删除数据...... :(
  • 很高兴能提供帮助。我必须说我不完全确定在锁定状态期间通知是否被重新发送或忽略。再说一次,我很高兴你指出了这一点,所以我可以确保无论如何都没有问题。希望您能够尽快同步您的 iCloud 数据。如果您仍然有问题,我建议您在这里提问。我知道我和其他很多人都非常乐意为您提供帮助
【解决方案5】:

更新:

每个人都应该看看 WWDC 2012 的 iCloud Core Data session 227。他们提供的源代码是基于 iCloud 的解决方案的优秀起点。真的花时间去经历他们正在做的事情。您将需要填补一些漏洞,例如将对象从一个存储复制到另一个存储和重复数据删除。话虽如此,我不再使用如下所述的migratePersistentStore 方法在本地和 iCloud 商店之间移动。


我原来的答案:

Slev 要求我发布一些代码,用于将商店从本地副本迁移到 iCloud 并再次返回。 此代码是实验性的,不应在生产中使用。此处仅供参考,以供我们分享和前进。一旦 Apple 发布了合适的参考应用程序,您可能应该参考它以了解您的模式和实践。

-(void) onChangeiCloudSync
{
    YourAppDelegate* appDelegate = (YourAppDelegate*) [[UIApplication sharedApplication] delegate];
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if ([iCloudUtility iCloudEnabled])
    {
        NSURL *storeUrl = [[appDelegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"YourApp2.sqlite"];
        NSURL *cloudURL = [fileManager URLForUbiquityContainerIdentifier:nil];
        NSString* coreDataCloudContent = [[cloudURL path] stringByAppendingPathComponent:@"data"];
        cloudURL = [NSURL fileURLWithPath:coreDataCloudContent];

        //  The API to turn on Core Data iCloud support here.
        NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@"com.yourcompany.yourapp.coredata", NSPersistentStoreUbiquitousContentNameKey, cloudURL, NSPersistentStoreUbiquitousContentURLKey, [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,nil];

        NSPersistentStore* store = [appDelegate.persistentStoreCoordinator.persistentStores objectAtIndex:0];
        NSError* error;
        if (![appDelegate.persistentStoreCoordinator migratePersistentStore:store toURL:storeUrl options:options withType:NSSQLiteStoreType error:&error])
        {
            NSLog(@"Error migrating data: %@, %@", error, [error userInfo]);
            //abort();
        }
        [fileManager removeItemAtURL:[[appDelegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"YourApp.sqlite"] error:nil];
        [appDelegate resetStore];
    }
    else
    {
        NSURL *storeUrl = [[appDelegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"YourApp.sqlite"];

        //  The API to turn on Core Data iCloud support here.
        NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                 [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                 [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
                                 nil];

        NSPersistentStore* store = [appDelegate.persistentStoreCoordinator.persistentStores objectAtIndex:0];
        NSError* error;
        if (![appDelegate.persistentStoreCoordinator migratePersistentStore:store toURL:storeUrl options:options withType:NSSQLiteStoreType error:&error])
        {
            NSLog(@"Error migrating data: %@, %@", error, [error userInfo]);
            //abort();
        }
        [fileManager removeItemAtURL:[[appDelegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"YourApp2.sqlite"] error:nil];
        [appDelegate resetStore];
    }
}

【讨论】:

  • 为了使上述更接近真正的解决方案,我们不应该将 sqlite 存储移动到与旧存储相同路径的 YourApp2.sqlite,而是移动到实际 ubiquity 容器中的 .nosync 文件夹。此外,removeItemAtURL 旧商店的决定是一个悬而未决的问题 - 必须非常小心,以确保您不会过早删除商店。此外,我们获取 store 的方式(通过获取 objectAtIndex:0)可能不是获取它的最佳方式。
  • 我花了很长时间才意识到你在这里发布了这个。这实际上与我个人一直在尝试的非常接近。目前,我已经让同步工作完美,但只有在手机设置中删除了我的应用程序的 iCloud 数据之后。在搞砸了迁移之后,我开始怀疑是否有必要,因为我们将 NSMigratePersistentStoreAutomaticallyOption 定义为 YES。我相信,这应该为我们做这一切。对于损坏的存储,我们可能需要手动切换一些,但我认为这些将是 NSMangedObject 项目而不是存储,如果这有意义的话
  • 我即将向苹果商店发布一个新应用程序,所以在发布这个答案将近 2 个月后,@kurtzmarc 你会选择什么,在没有 icloud 的情况下发布新应用程序,然后在何时更新它苹果给了我们一些指导,或者把它和这个问题中的所有补丁一起发布,如果我能让它们工作的话?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-13
  • 1970-01-01
  • 2017-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多