CloudKit 错误处理 - 重试逻辑
Posted
技术标签:
【中文标题】CloudKit 错误处理 - 重试逻辑【英文标题】:CloudKit error handling - retry logic 【发布时间】:2016-10-26 19:07:02 【问题描述】:我想按照 Apple 的要求将出色的 CloudKit 错误处理功能放入我的应用程序中。我现在想保存和修改一条记录。这是我的基本保存逻辑...
func addNewRecord(managedObj: NSManagedObject)
let newRec = managedObj.convertToCkRecord()
publicDB.saveRecord(newRec, completionHandler: saveHandler)
func saveHandler(savedRecord: CKRecord?, error: NSError?)
// handle errors here
if let error = error
if error.code == CKErrorCode.NotAuthenticated.rawValue
// debug
print("Not authentricated")
else if error.code == CKErrorCode.NetworkFailure.rawValue
print("Network failure!!")
if let retryAfterValue = error.userInfo[CKErrorRetryAfterKey] as? NSTimeInterval
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(retryAfterValue * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue())
// THIS IS WHERE I GET STUCK, WHERE DO I FIND THE FAILED CKRECORD FOR RETRY?
// IS IT IN USERINFO SOMEWHERE?
//self.saveHandler(savedRecord, error: error)
else
print("Save was a success! \(savedRecord)")
这似乎应该是基本的,但是我看到的每个示例都只有一个注释 //retry
或 //handle error
,包括在错误处理应该去的 WWDC 教程中。我想知道的是如何找到对我失败的 CKRecord 的引用?由于多线程问题,将其存储在局部变量中似乎不起作用。我尝试将它添加到队列中,但其他线程也可以进入该队列,所以我担心竞争条件。
【问题讨论】:
【参考方案1】:不要使用NSTimer
,而是使用dispatch_after
。
print("Network failure!!")
if let retryAfterValue = error.userInfo[CKErrorRetryAfterKey] as? NSTimeInterval
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(retryAfterValue * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue())
saveHandler(saveRecord: saveRecord, error: error)
这是我用于所有记录修改和删除的辅助方法(在 Objective-C 中)。它处理常见错误和重试。
- (void)modifyRecords:(NSArray<CKRecord *> *)records andDeleteRecords:(NSArray<CKRecordID *> *)deleteIds completion:(void (^)(NSArray<CKRecord *> *savedRecords, NSArray<CKRecordID *> *deletedRecordIDs, NSError *error))completion
CKModifyRecordsOperation *op = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:records recordIDsToDelete:deleteIds];
op.savePolicy = CKRecordSaveAllKeys;
op.modifyRecordsCompletionBlock = ^(NSArray *savedRecords, NSArray *deletedRecordIDs, NSError *operationError)
NSError *returnError = operationError;
if (operationError)
switch (operationError.code)
case CKErrorRequestRateLimited:
case CKErrorServiceUnavailable:
case CKErrorZoneBusy:
double delay = 3.0;
NSNumber *delayVal = operationError.userInfo[CKErrorRetryAfterKey];
if (delayVal)
delay = delayVal.doubleValue;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
[self modifyRecords:records andDeleteRecords:deleteIds completion:completion];
);
return;
case CKErrorPartialFailure:
if (savedRecords.count || deletedRecordIDs.count)
returnError = nil;
break;
default:
NSLog(@"Unhandled error in modify/deleteRecords: %@", operationError);
break;
if (completion)
completion(savedRecords, deletedRecordIDs, returnError);
;
[someCloudKitDatabase addOperation:op];
如果您只想添加/修改记录,请将nil
传递给deleteIds
参数。
这是 Swift 3 中相同的辅助方法(尚未经过测试,但除了最后一行之外它确实可以编译)。
func modifyRecords(_ records: [CKRecord]?, andDelete deleteIds: [CKRecordID]?, completionHandler: @escaping ([CKRecord]?, [CKRecordID]?, Error?) -> Void)
let op = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: deleteIds)
op.savePolicy = .allKeys
op.modifyRecordsCompletionBlock = (_ savedRecords: [CKRecord]?, _ deletedRecordIds: [CKRecordID]?, _ operationError: Error?) -> Void in
var returnError = operationError
if let ckerror = operationError as? CKError
switch ckerror
case CKError.requestRateLimited, CKError.serviceUnavailable, CKError.zoneBusy:
let retry = ckerror.retryAfterSeconds ?? 3.0
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + retry, execute:
modifyRecords(records, andDelete: deleteIds, completionHandler: completionHandler)
)
return
case CKError.partialFailure:
if (savedRecords != nil && savedRecords!.count > 0) || (deletedRecordIds != nil && deletedRecordIds!.count > 0)
returnError = nil
default:
break
completionHandler(savedRecords, deletedRecordIds, returnError)
someCloudKitDatabase.add(op)
【讨论】:
感谢 dispatch_after 提示。我的问题是 saveRecord 在失败期间为零。当我运行代码并关闭 wifi 以生成错误代码时。那是我的问题。 我会使用CKModifyRecordsOperation
,而不是CKDatabase
的基本saveRecord
方法。您拥有更多控制权,并且使用这种“重试”逻辑变得更加简单。
你能给我举个例子,CKModifyRecordsOperation
使重试逻辑更简单吗?
我有现有的 Objective-C 代码,但没有 Swift。如果你愿意,我可以发布 Objective-C 代码。你可以很容易地将它翻译成 Swift。
那太好了。我确实想出了如何让 saveRecord 按我的意愿工作,我必须将 completionHandler 逻辑内联,而不是放在它自己的单独函数中。以上是关于CloudKit 错误处理 - 重试逻辑的主要内容,如果未能解决你的问题,请参考以下文章