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

收到CKQuerySubscription通知后,如何获取CloudKit新创建记录?

问题分析与解决方案

你遇到的问题有两个核心原因:

  1. 逻辑错误:混淆了FriendRequest记录的ID与发送者用户的ID,导致查询条件完全错误
  2. CloudKit最终一致性:通知到达时,新创建的记录可能还未在所有节点完成同步,直接查询会出现"记录不存在"的情况

一、纠正核心逻辑错误

通知中拿到的recordID是新创建的FriendRequest记录本身的ID,不是发送者用户的ID。你之前用这个ID生成senderID引用的做法完全错误,这才是查询不到记录的主要原因。

正确的做法是直接用这个recordID获取对应的FriendRequest记录,再从记录中读取senderID、receiverID等字段:

func handleFriendRequest(recordID: CKRecord.ID) async {
    do {
        // 直接用recordID获取FriendRequest记录,无需查询
        let (record, _) = try await database.record(withID: recordID)
        
        guard let friendRequestRecord = record else {
            print("FriendRequest记录不存在")
            // 这里可以加入重试逻辑
            return
        }
        
        // 从记录中读取所需字段
        guard let senderID = friendRequestRecord["senderID"] as? CKRecord.Reference,
              let receiverID = friendRequestRecord["receiverID"] as? CKRecord.Reference else {
            print("记录字段不完整")
            return
        }
        
        // 验证接收者是否为当前用户(可选,确保通知是发给当前用户的)
        let userID = try await container.userRecordID()
        if receiverID.recordID == userID {
            print("成功获取好友请求")
            self.newPendingRequest = FriendRequest(record: friendRequestRecord)
        } else {
            print("该好友请求不属于当前用户")
        }
        
    } catch {
        print("获取好友请求失败: \(error)")
        // 这里处理错误,比如重试
    }
}

二、优雅处理CloudKit最终一致性问题

即使纠正了逻辑,仍可能因CloudKit的分布式同步延迟,导致通知到达时记录还未就绪。此时不建议用固定延迟,而是用指数退避重试机制:每次失败后等待时间翻倍,最多重试N次,既保证成功率又避免资源浪费。

示例重试逻辑:

// 封装带重试的记录获取方法
private func fetchRecordWithRetry(recordID: CKRecord.ID, maxRetries: Int = 3, currentRetry: Int = 0) async throws -> CKRecord {
    let retryDelay = Double(pow(2, Double(currentRetry))) // 指数退避:1s, 2s, 4s...
    
    do {
        let (record, _) = try await database.record(withID: recordID)
        guard let record = record else {
            throw NSError(domain: "CloudKitError", code: -1, userInfo: [NSLocalizedDescriptionKey: "记录不存在"])
        }
        return record
    } catch {
        if currentRetry < maxRetries {
            print("获取失败,\(retryDelay)s后重试...")
            try await Task.sleep(nanoseconds: UInt64(retryDelay * 1_000_000_000))
            return try await fetchRecordWithRetry(recordID: recordID, maxRetries: maxRetries, currentRetry: currentRetry + 1)
        } else {
            throw error // 超过重试次数,抛出错误
        }
    }
}

// 修改handleFriendRequest使用重试方法
func handleFriendRequest(recordID: CKRecord.ID) async {
    do {
        let friendRequestRecord = try await fetchRecordWithRetry(recordID: recordID)
        
        guard let senderID = friendRequestRecord["senderID"] as? CKRecord.Reference,
              let receiverID = friendRequestRecord["receiverID"] as? CKRecord.Reference else {
            print("记录字段不完整")
            return
        }
        
        let userID = try await container.userRecordID()
        if receiverID.recordID == userID {
            print("成功获取好友请求")
            self.newPendingRequest = FriendRequest(record: friendRequestRecord)
        } else {
            print("该好友请求不属于当前用户")
        }
        
    } catch {
        print("最终获取好友请求失败: \(error)")
        // 这里可以给用户提示,比如"获取好友请求失败,请稍后重试"
    }
}

三、额外优化建议

  • 利用通知的recordFields字段:如果你的CloudKit订阅配置了desiredKeys,可以直接从CKQueryNotification的recordFields中获取所需字段,无需再去CloudKit拉取记录,效率更高。例如:
    if let result = notification as? CKQueryNotification {
        if let recordFields = result.recordFields {
            // 直接从通知payload读取字段,比如senderID的recordName
            let senderRecordName = recordFields["senderID.recordName"] as? String
            // ...
        }
    }
    
  • 确保订阅配置正确:检查你的CKQuerySubscription是否设置了正确的predicate(比如只监听发给当前用户的FriendRequest),避免收到无关通知。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 18:53:19