2016-10-26 35 views
1

我想在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的參考?由於多線程問題,將它存儲在局部變量中似乎不起作用。我嘗試將它添加到隊列中,但其他線程也可以進入該隊列,所以我擔心競爭條件。

回答

4

而不是使用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