CloudKit错误处理 - 重试逻辑
问题描述:
我想在Apple应用程序中将出色的CloudKit错误处理功能放入我的应用程序中。我想现在保存并修改记录。这是我的基本逻辑保存...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的参考?由于多线程问题,将它存储在局部变量中似乎不起作用。我尝试将它添加到队列中,但其他线程也可以进入该队列,所以我担心竞争条件。
答
而不是使用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以生成错误代码时。这是我的问题。 – Coder1224
我会使用'CKModifyRecordsOperation'而不是'CKDatabase'的基本'saveRecord'方法。你有更多的控制权,并且使得使用这种“重试”逻辑变得更简单。 – rmaddy
你能指点我一个'CKModifyRecordsOperation'使重试逻辑更简单的例子吗? – Coder1224