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

CloudKit Fetch操作返回旧数据集的同步问题排查

CloudKit 同步后查询不到更新的问题排查与修复

看起来你在CloudKit和本地存储同步时遇到了一个典型问题:执行完新增/删除操作后,立刻查询却拿不到最新的记录。我帮你梳理几个关键问题点和修复方案:

1. 确保修改操作真正完成后再执行查询

你的saveUpdateCloud函数必须保证CKModifyRecordsOperation完全执行完毕才触发回调。如果操作还在CloudKit的队列中等待执行,就立刻调用fetchFromCloud,自然会拿到旧数据。

检查你的saveUpdateCloud实现,确保它正确设置了操作完成回调并加入到操作队列:

static func saveUpdateCloud(petsToSave: [CKRecord], recordIDsToDeleve: [CKRecordID], completion: @escaping (Bool, NSError?) -> ()) {
    let modifyOperation = CKModifyRecordsOperation(recordsToSave: petsToSave, recordIDsToDelete: recordIDsToDeleve)
    // 设置合适的保存策略,比如只同步修改过的字段
    modifyOperation.savePolicy = .changedKeys
    // 仅在操作彻底完成(成功/失败)后调用回调
    modifyOperation.modifyRecordsCompletionBlock = { savedRecords, deletedIDs, error in
        if let err = error as NSError? {
            completion(false, err)
        } else {
            completion(true, nil)
        }
    }
    // 将操作添加到CloudKit数据库的操作队列
    ArendK9DB.share.privateDB.add(modifyOperation)
}

2. 查询前清空本地数组,避免旧数据干扰

在fetchFromCloud的回调中,你直接往pets数组append新记录,但之前的sync函数清空数组后,后续查询可能会叠加旧数据。每次查询前先清空数组:

fetchFromCloud(){ (r , e) -> () in
    print("DEBUG Loading new CloudKit array at \(Date())")
    self.pets.removeAll() // 先清空旧数据,确保只保留最新查询结果
    if e != nil {
        print(e?.localizedDescription ?? "General Query Error: No Description")
    } else {
        for record in r {
            if let pet = Pet(remoteRecord: record) {
                self.pets.append(pet)
            }
        }
        // 保存到本地并回调,注意先判断数组非空
        print("DEGUG Saving records to local store")
        if !self.pets.isEmpty {
            self.pets[0].saveToLocal(petsToSave: self.pets)
        }
        print("DEBUG I have a new pet array from CloudKit \(self.pets)")
        completion(self.pets, nil)
    }
}

3. 强制查询忽略缓存,获取服务器最新数据

CloudKit默认会用缓存加速查询,这可能导致你拿到修改前的旧数据。修改fetchFromCloud中的查询,设置缓存策略为忽略缓存:

static func fetchFromCloud( completion: @escaping ( _ results: [CKRecord], _ error: NSError?) -> ()) {
    print("Fetch from CloudKit... starting completion handler")
    let predicate = NSPredicate(value: true)
    let query = CKQuery(recordType: RemoteRecords.pet, predicate: predicate)
    // 强制从服务器拉取最新数据,跳过本地缓存
    query.cachePolicy = .ignoreCache
    ArendK9DB.share.privateDB.perform(query, inZoneWith: ArendK9DB.share.zoneID, completionHandler: { (records, error) in
        if let err = error as NSError? {
            completion([], err)
        } else {
            completion(records ?? [], nil)
        }
    })
}

4. 修复本地新增记录的潜在崩溃风险

你创建changedRecord后直接用!强制解包,如果创建失败会导致崩溃。建议添加安全判断:

if localPet.recordName == "Local" {
    guard let newRecord = CKRecord(recordType: RemoteRecords.pet, zoneID: ArendK9DB.share.zoneID) else {
        continue // 跳过创建失败的记录
    }
    newRecord[RemotePet.petName] = localPet.petName as NSString
    newRecord[RemotePet.dob] = localPet.dob as NSDate?
    newRecord[RemotePet.petSex] = localPet.petSex as NSString?
    
    // 处理图片Asset的代码...
    if let photo = localPet.photo {
        let imageData = UIImageJPEGRepresentation(photo, 1.0)!
        let path = Pet.documentsDirectoryPath.appendingPathComponent(Pet.tempImageName)
        try? imageData.write(to: URL(fileURLWithPath: path), options: [.atomic])
        if let fileAsset = CKAsset(fileURL: URL(fileURLWithPath: path)) {
            newRecord[RemotePet.photo] = fileAsset
        }
    }
    petsToSave.append(newRecord)
}

5. 优化删除标记的判断逻辑

通过petName.lowercased().range(of: "delete me")标记删除的方式非常不可靠,建议在你的Pet本地模型中添加isMarkedForDelete: Bool属性,这样判断更准确,也避免误删正常记录。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:55:02