列举一些常见的错误,以及处理方式。
1.致命错误,基本上无能为力
public enum CKErrorCode : Int {
case internalError // 内容错误
case serverRejectedRequest // 服务器拒绝
case invalidArguments // 非法参数
case permissionFailure // 权限失败
}
2.服务器让你过一会儿再重试
public enum CKErrorCode : Int {
case zoneBusy
case serviceUnavailable
case requestRateLimited
}
在 userInfo
中通过 CKErrorRetryAfterKey
获取需要等待的时间,并重新初始化一个相同的 CKOperation 并重试
一个例子
var error = ... // Error from the previous CKOperation
if let retryAfter = error.userInfo[CKErrorRetryAfterKey] as? Double {
let delayTime = DispatchTime.now() + retryAfter
DispatchQueue.main.after(when: delayTime) {
// Initialize CKOperation for a retry
}
}
3.网络错误
public enum CKErrorCode : Int {
case networkUnavailable // 当前设备网络不能使用,通过监听网络状态之后重试
case networkFailure // 网络可以使用但是连接失败,稍后重试
}
4.CKOperation 中出现部分错误
public enum CKErrorCode : Int {
case partialFailure
case batchRequestFailed
}
因为一个 CKOperation 可以批量操作数据并且具有 原子性
,其中一条操作失败会造成整个 operation 的失败。比如使用 CKModifyRecordsOperation
修改 RecordA、RecordB、RecordC。但是其中 RecordA 失败了,RecordB、RecordC 成功了那么就会报 partialFailure
。在 operation.perRecordCompletionBlock
回调中RecordA 返回错误实际的错误,RecordB、RecordC 返回 batchRequestFailed
对于 partialFailure
可以通过 CKPartialErrorsByItemID
在 userInfo
中找到具体哪条操作报错
一个例子
let recordsToSave = [RecordA, RecordB, RecordC]
let operation = CKModifyRecordsOperation(recordsToSave:recordsToSave, recordIDsToDelete: nil)
operation.perRecordCompletionBlock = { (record, error) in
if let error = error as? CKError {
// 这里的 error 分别为
// RecordA -> serverRecordChanged
// RecordB -> batchRequestFailed
// RecordC -> batchRequestFailed
print(error)
}
}
operation.modifyRecordsCompletionBlock = { (records, recordID, error) in
if let error = error as? CKError {
// error 为 partialFailure
print(error)
// 这里 errorsByItemID 为 [RecordA.recordID: serverRecordChanged]
let errorsByItemID = error.userInfo[CKPartialErrorsByItemID] as? [CKRecord.ID: CKError]
}
}
self.privateDB.add(operation)
5.未登陆 iCloud
public enum CKErrorCode : Int {
case notAuthenticated
}
如果用户未登陆 iCloud 但是又要使用 CloudKit 会报错,比较好的是在每次应用启动时检查 iCloud 登陆状态
6.用户在设置中手动删除 iCloud 中数据
public enum CKErrorCode : Int {
case userDeletedZone
}
这个时候需要重新创建 zone 和 subscriptions
7.本地数据与服务器数据冲突
public enum CKErrorCode : Int {
case serverRecordChanged
}
当有多个设备使用更新同一块数据的时候,本地数据及其容易与服务器数据冲突。比如设备 A 和 设备B 都缓存有相同的 RecordA-1,但是设备 A 在某个时候修改成了 RecordA-2 并同步到 iCloud。之后设备 B 也要修改 RecordA-1,设备B 将其修改为 RecordA-3,这个时候 设备B 同步到 iCloud 时就会报错。
对于错误 serverRecordChanged
可以通过以下 key 在 userInfo
中找到相应的数据
Key | 备注 |
---|---|
CKRecordChangedErrorClientRecordKey | 获取本地尝试同步到服务器的 Record,是修改后的,即以上 RecordA-3 |
CKRecordChangedErrorServerRecordKey | 获取服务器上已经存在的 Record,即以上 RecordA-2 |
CKRecordChangedErrorAncestorRecordKey | 本地初始的数据,未进行任何修改的,即以上 RecordA-1 |
这个时候我们有两种解决方法
- 从服务器 fetch 最新的 Record,更新,并保存
- 修改 operation 的保存策略,operation 的
savePolicy
共有 3 种
Policy | 备注 |
---|---|
ifServerRecordUnchanged | 如果服务器数据有修改就报错 |
changedKeys | 本地数据覆盖服务器的数据,但是只修改有改动的 key |
allKeys | 本地数据覆盖服务器的数据,所有的 key 都同步为本地数据 |