2017-04-22 118 views
2

我正在尋找關於在Swift中處理CloudKit錯誤的一般建議,並且無法在線找到好的示例。這裏是我想知道的事情:處理CloudKit錯誤

1)每次出現錯誤的可能性時,我是否應該考慮每一種錯誤類型,還是不是真的有必要?

2)我已經讀過,處理CloudKit錯誤的一種常見方式是在錯誤消息提供的時間間隔後重試執行操作。這個重試基本上應該是我所有錯誤的標準程序嗎?

3)不同的CloudKit操作(保存,獲取等)會產生不同類型的錯誤,還是存在一組標準的CloudKit錯誤?

在此先感謝!我只是在尋找關於如何去解決CloudKit錯誤處理的一般信息,因爲我不確定從哪裏開始。

回答

4

我已經寫了一個CloudKit幫助,可以更容易地處理錯誤。這只是一個起點,還有很多可以做的事情。

此幫助程序在當前狀態下的主要重點是可以輕鬆地重試在適當的超時後應該重試的錯誤。

但是,您仍然需要處理不應該重試的錯誤,例如用戶的iCloud存儲空間已滿。即使使用這個幫助器,對這些幫助器方法之一的每次調用都需要正確處理結果,並可能向用戶報告錯誤。當然,您可以添加一個幫助方法來檢查所有可能的錯誤類型並顯示相應的消息。然後,CloudKit代碼的所有用途都可以調用該輔助方法。

這也只涉及一些可能的操作。您也想添加對其他操作的支持。最後,這還不能處理部分錯誤。這將是另一個有用的增強。

import Foundation 
import CloudKit 

public class CloudKitHelper { 
    private static func determineRetry(error: Error) -> Double? { 
     if let ckerror = error as? CKError { 
      switch ckerror { 
      case CKError.requestRateLimited, CKError.serviceUnavailable, CKError.zoneBusy, CKError.networkFailure: 
       let retry = ckerror.retryAfterSeconds ?? 3.0 

       return retry 
      default: 
       return nil 
      } 
     } else { 
      let nserror = error as NSError 
      if nserror.domain == NSCocoaErrorDomain { 
       if nserror.code == 4097 { 
        print("cloudd is dead") 

        return 6.0 
       } 
      } 

      print("Unexpected error: \(error)") 
     } 

     return nil 
    } 

    public static func modifyRecordZonesOperation(database: CKDatabase, recordZonesToSave: [CKRecordZone]?, recordZoneIDsToDelete: [CKRecordZoneID]?, modifyRecordZonesCompletionBlock: @escaping (([CKRecordZone]?, [CKRecordZoneID]?, Error?) -> Void)) { 
     let op = CKModifyRecordZonesOperation(recordZonesToSave: recordZonesToSave, recordZoneIDsToDelete: recordZoneIDsToDelete) 
     op.modifyRecordZonesCompletionBlock = { (savedRecordZones: [CKRecordZone]?, deletedRecordZoneIDs: [CKRecordZoneID]?, error: Error?) -> Void in 
      if let error = error { 
       if let delay = determineRetry(error: error) { 
        DispatchQueue.global().asyncAfter(deadline: .now() + delay) { 
         CloudKitHelper.modifyRecordZonesOperation(database: database, recordZonesToSave: recordZonesToSave, recordZoneIDsToDelete: recordZoneIDsToDelete, modifyRecordZonesCompletionBlock: modifyRecordZonesCompletionBlock) 
        } 
       } else { 
        modifyRecordZonesCompletionBlock(savedRecordZones, deletedRecordZoneIDs, error) 
       } 
      } else { 
       modifyRecordZonesCompletionBlock(savedRecordZones, deletedRecordZoneIDs, error) 
      } 
     } 
     database.add(op) 
    } 

    public static func modifyRecords(database: CKDatabase, records: [CKRecord], completion: @escaping (([CKRecord]?, Error?) -> Void)) { 
     CloudKitHelper.modifyAndDeleteRecords(database: database, records: records, recordIDs: nil) { (savedRecords, deletedRecords, error) in 
      completion(savedRecords, error) 
     } 
    } 

    public static func deleteRecords(database: CKDatabase, recordIDs: [CKRecordID], completion: @escaping (([CKRecordID]?, Error?) -> Void)) { 
     CloudKitHelper.modifyAndDeleteRecords(database: database, records: nil, recordIDs: recordIDs) { (savedRecords, deletedRecords, error) in 
      completion(deletedRecords, error) 
     } 
    } 

    public static func modifyAndDeleteRecords(database: CKDatabase, records: [CKRecord]?, recordIDs: [CKRecordID]?, completion: @escaping (([CKRecord]?, [CKRecordID]?, Error?) -> Void)) { 
     let op = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: recordIDs) 
     op.savePolicy = .allKeys 
     op.modifyRecordsCompletionBlock = { (savedRecords: [CKRecord]?, deletedRecordIDs: [CKRecordID]?, error: Error?) -> Void in 
      if let error = error { 
       if let delay = determineRetry(error: error) { 
        DispatchQueue.global().asyncAfter(deadline: .now() + delay) { 
         CloudKitHelper.modifyAndDeleteRecords(database: database, records: records, recordIDs: recordIDs, completion: completion) 
        } 
       } else { 
        completion(savedRecords, deletedRecordIDs, error) 
       } 
      } else { 
       completion(savedRecords, deletedRecordIDs, error) 
      } 
     } 
     database.add(op) 
    } 
} 
3

是的,你想檢查每個cloudkit調用錯誤。 Apple在雲端工具相關的WWDC視頻中強調了這一點。

當您檢測到錯誤時所做的工作差異很大。重試有時是一種選擇,但有時不合適。如果您正在使用批處理操作,則重試可能需要一些額外的工作才能提取失敗的記錄。所以,是的,你可能有時想重試,但不是,你可能不會自動重試每一次失敗的操作。

有一組錯誤,在CKError.h中定義。但是,你並不總是得到一個CKError。有時候,特別是CKErrorPartialFailure,您會得到一個頂級錯誤,其中包含您也必須展開的嵌套錯誤。由於IOS 10的,錯誤的CKError.h列表的樣子:

typedef NS_ENUM(NSInteger, CKErrorCode) { 
    CKErrorInternalError     = 1, /* CloudKit.framework encountered an error. This is a non-recoverable error. */ 
    CKErrorPartialFailure     = 2, /* Some items failed, but the operation succeeded overall. Check CKPartialErrorsByItemIDKey in the userInfo dictionary for more details. */ 
    CKErrorNetworkUnavailable    = 3, /* Network not available */ 
    CKErrorNetworkFailure     = 4, /* Network error (available but CFNetwork gave us an error) */ 
    CKErrorBadContainer     = 5, /* Un-provisioned or unauthorized container. Try provisioning the container before retrying the operation. */ 
    CKErrorServiceUnavailable    = 6, /* Service unavailable */ 
    CKErrorRequestRateLimited    = 7, /* Client is being rate limited */ 
    CKErrorMissingEntitlement    = 8, /* Missing entitlement */ 
    CKErrorNotAuthenticated    = 9, /* Not authenticated (writing without being logged in, no user record) */ 
    CKErrorPermissionFailure    = 10, /* Access failure (save, fetch, or shareAccept) */ 
    CKErrorUnknownItem     = 11, /* Record does not exist */ 
    CKErrorInvalidArguments    = 12, /* Bad client request (bad record graph, malformed predicate) */ 
    CKErrorResultsTruncated NS_DEPRECATED(10_10, 10_12, 8_0, 10_0, "Will not be returned") = 13, 
    CKErrorServerRecordChanged   = 14, /* The record was rejected because the version on the server was different */ 
    CKErrorServerRejectedRequest   = 15, /* The server rejected this request. This is a non-recoverable error */ 
    CKErrorAssetFileNotFound    = 16, /* Asset file was not found */ 
    CKErrorAssetFileModified    = 17, /* Asset file content was modified while being saved */ 
    CKErrorIncompatibleVersion   = 18, /* App version is less than the minimum allowed version */ 
    CKErrorConstraintViolation   = 19, /* The server rejected the request because there was a conflict with a unique field. */ 
    CKErrorOperationCancelled    = 20, /* A CKOperation was explicitly cancelled */ 
    CKErrorChangeTokenExpired    = 21, /* The previousServerChangeToken value is too old and the client must re-sync from scratch */ 
    CKErrorBatchRequestFailed    = 22, /* One of the items in this batch operation failed in a zone with atomic updates, so the entire batch was rejected. */ 
    CKErrorZoneBusy      = 23, /* The server is too busy to handle this zone operation. Try the operation again in a few seconds. */ 
    CKErrorBadDatabase     = 24, /* Operation could not be completed on the given database. Likely caused by attempting to modify zones in the public database. */ 
    CKErrorQuotaExceeded     = 25, /* Saving a record would exceed quota */ 
    CKErrorZoneNotFound     = 26, /* The specified zone does not exist on the server */ 
    CKErrorLimitExceeded     = 27, /* The request to the server was too large. Retry this request as a smaller batch. */ 
    CKErrorUserDeletedZone    = 28, /* The user deleted this zone through the settings UI. Your client should either remove its local data or prompt the user before attempting to re-upload any data to this zone. */ 
    CKErrorTooManyParticipants   NS_AVAILABLE(10_12, 10_0) = 29, /* A share cannot be saved because there are too many participants attached to the share */ 
    CKErrorAlreadyShared     NS_AVAILABLE(10_12, 10_0) = 30, /* A record/share cannot be saved, doing so would cause a hierarchy of records to exist in multiple shares */ 
    CKErrorReferenceViolation    NS_AVAILABLE(10_12, 10_0) = 31, /* The target of a record's parent or share reference was not found */ 
    CKErrorManagedAccountRestricted  NS_AVAILABLE(10_12, 10_0) = 32, /* Request was rejected due to a managed account restriction */ 
    CKErrorParticipantMayNeedVerification NS_AVAILABLE(10_12, 10_0) = 33, /* Share Metadata cannot be determined, because the user is not a member of the share. There are invited participants on the share with email addresses or phone numbers not associated with any iCloud account. The user may be able to join the share if they can associate one of those email addresses or phone numbers with their iCloud account via the system Share Accept UI. Call UIApplication's openURL on this share URL to have the user attempt to verify their information. */ 
} NS_ENUM_AVAILABLE(10_10, 8_0); 

一種方法,當你正在開發和測試應用程序,是檢查每一個cloudkit操作錯誤,如果檢測到火的NS停止應用程序。然後,檢查錯誤,底層錯誤和上下文,以確定它失敗的原因以及您需要對此做些什麼。最有可能的是,隨着時間的推移,你會看到常見的模式出現,然後你可以考慮構建一個通用的錯誤處理程序。