我的应用程序使用CoreData + CloudKit同步。一些CoreData实体(如Item )可以通过iCloud的共享数据库共享。该应用程序只使用1 NSPersistentContainer,但它有2 NSManagedContexts,visualContext和backgroundContext。
因此,在保存上下文期间,可能会出现两种类型的合并冲突: 1)如果两个上下文试图在不同状态下保存相同的Item,2)如果我的持久容器和iCloud同步试图在不同状态下保存相同的Item。
Item有一个属性updatedAt,应用程序要求始终保存最后更新的Item版本。
由于一致性原因,我不能按属性合并。只能存储完整的Item对象,无论是存储在托管上下文中的对象,还是存储在托管上下文中的对象,或者是持久存储的对象。
但是不能使用标准的合并策略:NSRollbackMergePolicy忽略托管上下文中的更改,并接受持久副本,而NSOverwriteMergePolicy则用托管上下文中的对象覆盖持久存储。但是我必须使用Item和最新的updatedAt。因此,我必须使用自定义合并策略。
要找到如何做这件事的任何提示都不容易。我找到了两个带有演示代码的教程。最好的一本是Florian和Daniel的“核心数据”一书,这本书有关于自定义合并策略和相关代码这里的一节。另一篇是代码的迪皮卡·拉梅什的帖子。然而,我不得不承认,我并没有完全理解两者。但基于它们的代码,我尝试设置自己的自定义合并策略,该策略将分配给两个托管上下文的mergePolicy属性。下面是:
import CoreData
protocol UpdateTimestampable {
var updatedAt: Date? { get set }
}
class NewestItemMergePolicy: NSMergePolicy {
init() {
super.init(merge: .overwriteMergePolicyType)
}
override open func resolve(optimisticLockingConflicts list: [NSMergeConflict]) throws {
let nonItemConflicts = list.filter({ $0.sourceObject.entity.name != Item.entityName })
try super.resolve(optimisticLockingConflicts: nonItemConflicts)
let itemConflicts = list.filter({ $0.sourceObject.entity.name == Item.entityName })
itemConflicts.forEach { conflict in
guard let sourceObject = conflict.sourceObject as? UpdateTimestampable else { fatalError("must be UpdateTimestampable") }
let key = "updatedAt"
let sourceObjectDate = sourceObject.updatedAt ?? .distantPast
let objectDate = conflict.objectSnapshot?[key] as? Date ?? .distantPast
let cachedDate = conflict.cachedSnapshot?[key] as? Date ?? .distantPast
let persistedDate = conflict.persistedSnapshot?[key] as? Date ?? .distantPast
let latestUpdateAt = [sourceObjectDate, objectDate, cachedDate, persistedDate].max()
let persistedDateIsLatest = persistedDate == latestUpdateAt
let sourceObj = conflict.sourceObject
if let context = sourceObj.managedObjectContext {
context.performAndWait {
context.refresh(sourceObj, mergeChanges: !persistedDateIsLatest)
}
}
}
try super.resolve(optimisticLockingConflicts: itemConflicts)
}
} 我的第一个问题是这段代码是否有意义。我之所以这样问,是因为合并冲突很难测试。
具体来说,我显然必须使用super.init(merge: .overwriteMergePolicyType)中的任何标准合并属性,尽管这显然并不重要,因为我使用的是自定义合并冲突解决方案。
https://stackoverflow.com/questions/71297240
复制相似问题