You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何安全清理CoreData持久化历史不影响CloudKit同步正确性

我们当前通过NSPersistentCloudKitContainer实现本地CoreData与CloudKit的集成。

启用持久化历史跟踪的原因

参考相关技术问题描述,为避免同步异常,我们需要开启NSPersistentHistoryTrackingKey。

持久化历史清理需求

根据苹果官方CoreData相关文档说明,持久化历史跟踪产生的历史数据需要手动执行清理操作。
但目前我们尚未明确如何安全执行历史清理,避免影响CloudKit同步的正确性,为此我们搭建了如下测试环境开展验证:

  • 启动模拟器,在模拟器中执行数据插入操作
  • 启动真机,真机将因模拟器侧的数据插入操作接收静默推送通知
  • 模拟器与真机运行完全相同的代码逻辑
  • 每次在模拟器插入数据后,观察真机的同步表现

测试1:处理完成后立即清理所有历史数据

测试代码如下:

@objc func storeRemoteChange(_ notification: Notification) {
    // Process persistent history to merge changes from other coordinators.
    historyQueue.addOperation {
        self.processPersistentHistory()
    }
}

/**
 Process persistent history, posting any relevant transactions to the current view.
 */
private func processPersistentHistory() {
    backgroundContext.performAndWait {
        
        // Fetch history received from outside the app since the last token
        let historyFetchRequest = NSPersistentHistoryTransaction.fetchRequest!
        historyFetchRequest.predicate = NSPredicate(format: "author != %@", appTransactionAuthorName)
        let request = NSPersistentHistoryChangeRequest.fetchHistory(after: lastHistoryToken)
        request.fetchRequest = historyFetchRequest

        let result = (try? backgroundContext.execute(request)) as? NSPersistentHistoryResult
        guard let transactions = result?.result as? [NSPersistentHistoryTransaction] else { return }

        ...
        
        // Update the history token using the last transaction.
        lastHistoryToken = transactions.last!.token
        
        // Remove history before the last history token
        let purgeHistoryRequest = NSPersistentHistoryChangeRequest.deleteHistory(before: lastHistoryToken)
        do {
            try backgroundContext.execute(purgeHistoryRequest)
        } catch {
            error_log(error)
        }
    }
}

测试观察结果:真机获取的CloudKit同步信息异常,出现数据重复或数据被删除的问题。
我们对该问题的假设如下:

  • 持久化历史数据在同一设备的多个持久化协调器之间共享
  • 业务层可见的协调器完成事务处理后,记录lastHistoryToken,随即清理该token之前的所有历史
  • 但CloudKit同步使用了一个系统层面不可见的内置协调器,极有可能在清理历史时,该协调器尚未处理被删除的历史事务
  • 最终导致CloudKit同步真机数据时缺少必要的事务历史,引发数据异常

测试2:处理完成后仅清理2分钟前的历史数据

我们调整清理逻辑,仅删除生成时间早于2分钟的事务历史,核心代码如下:

// Remove history older than 2 minutes.
let date = Date(timeMillis: Date.currentTimeMillis - 2*60*1000)
let purgeHistoryRequest = NSPersistentHistoryChangeRequest.deleteHistory(before: date)
do {
    try backgroundContext.execute(purgeHistoryRequest)
} catch {
    error_log(error)
}

测试观察结果:

  • 若两次storeRemoteChange触发的时间间隔小于2分钟,真机可获取正确的CloudKit同步信息
  • 若两次storeRemoteChange触发的时间间隔大于2分钟,真机将获取错误的CloudKit同步信息,同样出现数据重复或丢失问题

问题汇总

参考相关技术问答给出的建议:“在持久化历史处理完成7天后再执行清理是安全的”。
但针对单用户双设备的场景:

  • 用户在常用设备A上频繁执行读写操作
  • 用户间隔7天才在闲置设备B上启动同一款应用
    按照测试2的观测结论,是否意味着设备B会出现CloudKit同步信息错误?如果结论成立,请问如何正确执行持久化历史清理操作,才不会影响CloudKit同步的正确性?

附:测试2复现步骤

  • 下载并运行苹果官方提供的CoreData CloudKit同步示例项目
  • 将项目中的CoreDataStack.swift替换为自定义实现文件,该文件包含Date辅助方法与2分钟历史清理逻辑
  • 在模拟器中点击右上角按钮创建1条记录,可观察到真机同步得到该条记录
  • 等待3分钟后再次点击右上角按钮创建记录,此时模拟器中共有2条记录,但真机中的数据会完全丢失

测试现象参考:左侧为真机界面,右侧为模拟器界面,真机数据完全丢失,与模拟器数据状态不一致


内容的提问来源于stack exchange,提问作者Cheok Yan Cheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 21:12:35