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]; 
} 

如果你只是想添加/修改记录,通过nildeleteIds参数。

这里是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) 
} 
+0

感谢dispatch_after提示。我的问题是在失败期间saveRecord为零。当我运行代码并关闭WiFi以生成错误代码时。这是我的问题。 – Coder1224

+0

我会使用'CKModifyRecordsOperation'而不是'CKDatabase'的基本'saveRecord'方法。你有更多的控制权,并且使得使用这种“重试”逻辑变得更简单。 – rmaddy

+0

你能指点我一个'CKModifyRecordsOperation'使重试逻辑更简单的例子吗? – Coder1224