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

CloudKit使用自定义CKRecord.ID可删除记录却无法修改的问题

问题原因
  • CloudKit 每条记录除了自定义的ID外,还内置了服务端维护的变更标记(change tag),用来做并发修改冲突校验。删除操作仅需校验记录ID匹配即可执行,因此你用正确的ID可以完成删除。
  • 你修改时直接通过CKRecord(recordType: recordType, recordID: recordId)创建了全新的记录实例,这个实例没有携带服务端当前对应记录的有效change tag。CloudKit默认的保存策略是ifServerRecordUnchanged,会校验你提交的记录携带的change tag和服务端最新版本是否一致:如果不一致、或者像你的情况根本没有change tag,就会判定为「记录已存在且版本不匹配」,抛出Server Record Changed和record to insert already exists错误。
解决方案

有两种常用的修复方案:

方案1:强制覆盖服务端版本(适合单用户使用、不会有并发修改的场景)

直接修改CKModifyRecordsOperation的保存策略,跳过change tag校验,强制用你提交的内容覆盖服务端记录。
只要在你的modify方法中添加一行配置即可:

let operation = CKModifyRecordsOperation(recordsToSave: [record], recordIDsToDelete: nil)
operation.configuration = configuration
// 新增这行,修改保存策略为强制覆盖
operation.savePolicy = .overwriteAll

方案2:先拉取服务端最新记录再修改(适合多端同步、可能存在并发修改的场景)

先通过ID从CloudKit查询到最新的记录实例,在这个实例基础上修改字段内容再提交,此时实例已经携带了正确的change tag,不会触发冲突校验报错。
示例代码逻辑如下:

func modify(_ item: MyItem, completion: @escaping (Result<Int, Error>) -> ()) {
    let myOwnUUID = item.id
    let recordId = CKRecord.ID(recordName: myOwnUUID, zoneID: .default)
    // 先拉取服务端最新记录
    ckDatabase.fetch(withRecordID: recordId) { record, error in
        DispatchQueue.main.async {
            if let error = error {
                completion(.failure(error))
                return
            }
            guard let record = record else {
                completion(.failure(CloudKitHelperError.recordNotFound))
                return
            }
            // 在拉取到的记录上修改字段
            record.setObject(item.title as __CKRecordObjCValue, forKey: "title")
            // 再提交修改
            let configuration = CKOperation.Configuration()
            configuration.timeoutIntervalForRequest = 10
            configuration.timeoutIntervalForResource = 10
            let operation = CKModifyRecordsOperation(recordsToSave: [record], recordIDsToDelete: nil)
            operation.configuration = configuration
            operation.modifyRecordsCompletionBlock = { (savedRecords, _, error) in
                DispatchQueue.main.async {
                    if savedRecords != nil, error == nil {
                        completion(.success(0))
                    }
                    if let error = error {
                        completion(.failure(error))
                    }
                }
            }
            ckDatabase.add(operation)
        }
    }
}

内容的提问来源于stack exchange,提问作者Kaplan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 08:45:01