全局标识符? - iCloud + Core Data + Ensembles - 删除对象时重复

Kre*_*erg 5 core-data identifier duplicates icloud ensembles

我正在尝试在我的Core Data应用程序中实现iCloud同步.我不是那么专业的编程,这真的是我学到的一个高级主题......我发现Drew McCormack的核心数据同步框架"Ensembles".它似乎使iCloud Sync更容易.

我将它集成到我的应用程序中,只要我将新对象添加到我的Core Data模型中,同步就能很好地工作.但是当我删除一个对象时,它会创建重复项.然后从重复复制.我最终拥有3-4次相同的条目(对象)......

这是为什么?我究竟做错了什么?我做了一些研究,我的猜测是全局标识符可以解决这个问题吗?

什么是全局标识符?我猜他们有助于避免重复!?但是我该怎么设置呢?我真的不知道,做了很多研究但却找不到答案.

感谢帮助!

更新: 感谢您的帮助!我读了自述书和书,但因为我是初学者,所以一切都不清楚.

我想我现在理解在Ensembles中使用全局标识符,但我不知道我是否正确地使用它.

如果我理解正确,我必须为每个对象分配一个标识符.我可以通过将其存储在属性中来实现.只要它是唯一的NSString,这个标识符可以是任何东西吗?

在我的应用程序中,用户可以存储不同的东西,假设名称,文本,标题,日期等.该应用程序基于Xcode中的Master-Detail-View模板,并使用Core Data.我的核心数据模型只有一个具有一些属性的实体,大多数是字符串和NSDate.没有关系或任何东西.如果用户点击"+",则创建一个新对象,并将用户输入的内容存储在属性中.

我添加全局标识符的做法是添加一个存储它的新属性.所以当我创建一个新对象时

/// I did find that to use as identifier !?

NSString *taskUniqueStringKey = newManagedObject.objectID.URIRepresentation.absoluteString;

/// and store it in the attribute.

[newManagedObject setValue:taskUniqueStringKey forKey:@"coreDataObjectID"]; 
Run Code Online (Sandbox Code Playgroud)

然后我用这个:

- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble globalIdentifiersForManagedObjects:(NSArray *)objects
{

return [objects valueForKeyPath:@"coreDataObjectID"];;

}
Run Code Online (Sandbox Code Playgroud)

这似乎对我有用.但我做得对吗?这是分配全局标识符的正确位置吗?我没有awakeFromInsert!

如果这个工作,我得到了下一个问题.我的应用已经存在,用户在更新之前保存的旧条目将缺少全局标识符.我该怎么办?我认为我已经得到了什么以及什么是唯一的,我唯一能想到的是在创建对象时保存[NSDate date]的属性.

我试图使用它,但我失败了因为Ensembles只会接受NSString而不是NSDate!?我可以使用这个日期属性,这是否足够独特并且可以作为gloabl标识符使用?如果是的话,请你给我一些如何将这个从日期转换为字符串的代码示例?

与Ensembles同步非常有效.没有重复,您可以关闭iCloud并保留条目并再次打开它,它会像它应该同步而不会丢失本地存储的对象左右.合奏真的很酷!我看到一些小的奇怪的行为,有时同步需要很长时间,有时它真的很快,如果我在两个不同的设备上在短时间内编辑的东西,它有点混乱像我刚刚删除的对象重新出现.但我觉得这很正常吗?如果我在不同的设备上使用应用程序之间花一些时间一切正常.

我理解正确,只有一种方法可以调用同步:

- (void)syncWithCompletion:(void(^)(void))completion
{
if (self.ensemble.isMerging) return;


if (!self.ensemble.isLeeched) {
    [self.ensemble leechPersistentStoreWithCompletion:^(NSError *error) {
        if (error) NSLog(@"Error in leech: %@", error);

        if (completion) completion();
    }];
}
else {
    [self.ensemble mergeWithCompletion:^(NSError *error) {

        if (completion) completion();
    }];
}
Run Code Online (Sandbox Code Playgroud)

如果需要你就打电话给它?没有其他事情像以前做过合并而没有先行,或者像"这是实际状态 - 像现在一样保存它"这样的方法吗?

您希望同步的应用中有不同的点.在应用程序启动时和终止时将是一个好点.在我的应用程序中有两点我应该同步我猜:当添加一个对象并将其保存到Core Data时以及当我保存对象的更改时.我还可以提供"立即同步"按钮.这是一个很好的方法,我总是打电话

[self syncWithCompletion:NULL];
Run Code Online (Sandbox Code Playgroud)

提出的另一个问题.我可以从Ensembles中排除对象的同步吗?我的应用程序在第一次应用启动时将教程条目作为对象加载 如果可能的话,我不想同步他们?

非常感谢你的帮助!如果我可以用德语本地化等帮助你,请告诉我!;)

Dre*_*ack 6

是的,这几乎可以肯定是因为没有为您的对象设置全局标识符,或者至少没有正确地执行它.

当你整合你的集合时,本地持久存储被导入到同步数据中.如果没有全局标识符,Ensembles会为您的对象分配随机ID,因此它可以跨设备跟踪它们.

当您使用具有相同数据的第二个设备时,会出现重复项.Ensembles无法知道数据表示与其他设备上相同的逻辑对象,因此它再次分配随机ID.实际上,它将每个设备上的对象视为完全独立,因此在同步后所有对象都会在数据集中结束.

解决方案是全局标识符.通过实现CDEPersistentStoreEnsemble委托方法,您可以为Ensembles提供全局ID,它可以用来标识不同设备上的哪些对象属于一起.

您应该为全球ID使用什么?通常,只是一个UUID,虽然对于像单件一样的对象,你只想选择一个id.

你可以初始化它们awakeFromInsert.您可以将全局ID存储在实体的属性中.(请注意,如果要迁移现有应用程序,则在尝试使用商店进行同步之前,如果已生成全局ID,则需要使用提取进行检查.)

更多细节在GitHub上的README和leanpub的书中.

更新

要回答您的更新问题:

是的,标识符只需要是一个字符串,并且是不可变的.一旦分配,它不应该改变.

NSManagedObjectID不是一个非常好的全局标识符,因为它在不同的设备上会有所不同.我们真的想要跨设备的全球性东西.

如果您从头开始,使用NSUUID是一种很好的方法.只需创建一个唯一的ID,并将其存储在对象中.

如果您有一个现有应用程序,并且它已通过其他机制进行同步,则需要提供一种在每个设备上提供相同全局标识符的方法.一种方法是以某种方式混搭对象属性.通常,这将为您提供非常接近于独特的价值,并且对于过渡来说它将足够好.

例如,您可以快速获取,并发现您的对象还没有全局ID.您浏览对象,并将全局ID设置为由creationDate + text组成的字符串.(您甚至可以通过获取哈希来缩短它,但它可能并不重要.)在初始"迁移"到全局标识符之后,您只需将UUID用于任何新创建的对象.

请注意,您不必使用awakeFromInsert.这只是一个方便的地方.只要在保存对象之前分配全局标识符就应该没问题.

从a获取字符串的最简单方法NSDate是调用description方法,但另一种方法是获取double使用timeIntervalSince1970,并将其转换为字符串.(小心将日期作为唯一标识符:通常一起创建的对象将具有相同的创建日期.)

你对如何进行同步是正确的:你可以简单地打电话syncWithCompletion:.

回答有关排除对象的问题:您不能排除单个对象,主要是因为当这些对象与同步对象有关系时,它可能会变得棘手.您可以通过以下两种方式之一处理这些对象:

  1. 将它们放在单独的持久性存储中,并将该存储添加到同一个持久性存储协调器.
  2. 同步对象,但手动为它们提供全局ID,以便在每个设备上对对象进行相同处理.例如.您可以将全局ID设为"Sample1","Sample2"等.