2016-10-26 10 views
1

애플이 원하는 것처럼 우수한 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)") 
    } 

}

이이 기본적인해야 것입니다,하지만 난 이것에 대해 볼 때마다 예는 바로 WWDC 튜토리얼에 포함 댓글 //retry 또는 //handle error을 가지고 어디에 오류 처리가 필요합니다. 내가 알고 싶은 것은 실패한 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]; 
} 

만/추가 기록을 수정 deleteIds 매개 변수에 nil를 전달하려는 경우

.

스위프트 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는 실패하는 동안 nil입니다. 코드를 실행하고 wifi를 끄면 오류 코드가 생성됩니다. 그게 내 문제 야. – Coder1224

+0

나는'CKDatabase'의 기본적인'saveRecord' 메쏘드 대신'CKModifyRecordsOperation'을 사용할 것입니다. 당신은 훨씬 더 많은 통제권을 가지며이 "재시도"논리를 사용하는 것이 훨씬 더 간단합니다. – rmaddy

+0

당신은'CKModifyRecordsOperation'가 재시도 로직을 더 단순하게 만드는 예제를 지적 할 수 있습니까? – Coder1224