收到CKQuerySubscription通知后,如何获取CloudKit新创建记录?
问题分析与解决方案
你遇到的问题有两个核心原因:
- 逻辑错误:混淆了
FriendRequest记录的ID与发送者用户的ID,导致查询条件完全错误 - 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
相关产品推荐
相关产品推荐

