首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何实现CoreData自定义合并策略?

如何实现CoreData自定义合并策略?
EN

Stack Overflow用户
提问于 2022-02-28 15:23:55
回答 1查看 350关注 0票数 1

我的应用程序使用CoreData + CloudKit同步。一些CoreData实体(如Item )可以通过iCloud的共享数据库共享。该应用程序只使用1 NSPersistentContainer,但它有2 NSManagedContextsvisualContextbackgroundContext

因此,在保存上下文期间,可能会出现两种类型的合并冲突: 1)如果两个上下文试图在不同状态下保存相同的Item,2)如果我的持久容器和iCloud同步试图在不同状态下保存相同的Item

Item有一个属性updatedAt,应用程序要求始终保存最后更新的Item版本。

由于一致性原因,我不能按属性合并。只能存储完整的Item对象,无论是存储在托管上下文中的对象,还是存储在托管上下文中的对象,或者是持久存储的对象。

但是不能使用标准的合并策略:NSRollbackMergePolicy忽略托管上下文中的更改,并接受持久副本,而NSOverwriteMergePolicy则用托管上下文中的对象覆盖持久存储。但是我必须使用Item和最新的updatedAt。因此,我必须使用自定义合并策略。

要找到如何做这件事的任何提示都不容易。我找到了两个带有演示代码的教程。最好的一本是Florian和Daniel的“核心数据”一书,这本书有关于自定义合并策略和相关代码这里的一节。另一篇是代码的迪皮卡·拉梅什的帖子。然而,我不得不承认,我并没有完全理解两者。但基于它们的代码,我尝试设置自己的自定义合并策略,该策略将分配给两个托管上下文的mergePolicy属性。下面是:

代码语言:javascript
运行
复制
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)中的任何标准合并属性,尽管这显然并不重要,因为我使用的是自定义合并冲突解决方案。

EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71297240

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档