【问题标题】:Error Handling When Saving a CKRecord保存 CKRecord 时的错误处理
【发布时间】:2015-12-13 18:57:48
【问题描述】:

我正在寻找保存CKRecord 时正确处理错误的示例。根据Apple docs,我应该“使用错误对象中的信息来确定问题是否有解决方法。”

我知道错误对象有一个userInfo 字典,但我如何确定字典的键是什么以及如何处理错误?

以下示例说明了我当前如何保存CKRecord

    CKRecord *record = [[CKRecord alloc] initWithRecordType:@"MyRecordType"];
[record setValue:[NSNumber numberWithInt:99] forKey:@"myInt"];

[db saveRecord:record completionHandler:^(CKRecord *savedPlace, NSError *error) {
    // handle errors here
    if (savedPlace) {
        NSLog(@"save successful");
    }else{
        NSLog(@"save unsuccessful");
    }
    if (error) {
        NSLog(@"Error saving %@", error.localizedDescription);
    }

}];

如何改进此代码以解决潜在的保存问题?

【问题讨论】:

  • 1) 有关NSError code 属性的可能值,请参阅CKErrorCode 的文档。错误上的userInfo 仅用于其中几个代码。 2) 您的if 支票错误。只有error == nilCKDatabase saveRecord:completionHandler: 的文档中所述,它才会成功。

标签: ios cloudkit nserror


【解决方案1】:

这是一个实现,其中我处理CKErrorNetworkFailure 的常见错误,方法是在存储在userInfo 字典中的建议的时间间隔后重试后重试保存。

-(void)saveRecord:(CKRecord*)record toDatabase:(CKDatabase*)database{
[database saveRecord:record completionHandler:^(CKRecord *record, NSError *error) {
    if (error==nil) {
        NSLog(@"The save was successful");
        //Do something
    }else{
        NSLog(@"Error saving with localizedDescription: %@", error.localizedDescription);
        NSLog(@"CKErrorCode = %lu", [error code]);

        if ([error code]==CKErrorNetworkFailure) {
            double retryAfterValue = [[error.userInfo valueForKey:CKErrorRetryAfterKey] doubleValue];
            NSLog(@"Error code network unavailable retrying after %f", retryAfterValue);
            NSTimer *timer = [NSTimer timerWithTimeInterval:retryAfterValue target:self selector:@selector(testOutCloudKit) userInfo:nil repeats:NO];
            [timer fire];
        }
    }
}];

}

【讨论】:

    【解决方案2】:

    在我的库EVCloudKitDao 中,我有一个单独的方法,它将根据错误代码返回错误类型。根据该类型,您可以决定要做什么。这是那个方法:

    public enum HandleCloudKitErrorAs {
        case Success,
        Retry(afterSeconds:Double),
        RecoverableError,
        Fail
    }
    
    public static func handleCloudKitErrorAs(error:NSError?, retryAttempt:Double = 1) -> HandleCloudKitErrorAs {
        if error == nil {
            return .Success
        }
        let errorCode:CKErrorCode = CKErrorCode(rawValue: error!.code)!
        switch errorCode {
        case .NetworkUnavailable, .NetworkFailure, .ServiceUnavailable, .RequestRateLimited, .ZoneBusy, .ResultsTruncated:
            // Use an exponential retry delay which maxes out at half an hour.
            var seconds = Double(pow(2, Double(retryAttempt)))
            if seconds > 1800 {
                seconds = 1800
            }
            // Or if there is a retry delay specified in the error, then use that.
            if let userInfo = error?.userInfo {
                if let retry = userInfo[CKErrorRetryAfterKey] as? NSNumber {
                    seconds = Double(retry)
                }
            }
            NSLog("Debug: Should retry in \(seconds) seconds. \(error)")
            return .Retry(afterSeconds: seconds)
        case .UnknownItem, .InvalidArguments, .IncompatibleVersion, .BadContainer, .MissingEntitlement, .PermissionFailure, .BadDatabase, .AssetFileNotFound, .OperationCancelled, .NotAuthenticated, .AssetFileModified, .BatchRequestFailed, .ZoneNotFound, .UserDeletedZone, .InternalError, .ServerRejectedRequest, .ConstraintViolation:
            NSLog("Error: \(error)")
            return .Fail;
        case .QuotaExceeded, .LimitExceeded:
            NSLog("Warning: \(error)")
            return .Fail;
        case .ChangeTokenExpired,  .ServerRecordChanged:
            NSLog("Info: \(error)")
            return .RecoverableError
        default:
            NSLog("Error: \(error)") //New error introduced in iOS...?
            return .Fail;
        }
    }
    

    在 CloudKit 方法的回调中,您可以像这样使用这个函数:

    func loadContacts(retryCount:Double = 1) {        
        // Look who of our contact is also using this app.
        EVCloudKitDao.publicDB.allContactsUserInfo({ users in
                EVLog("AllContactUserInfo count = \(users.count)");
                Async.main{
                    self.contacts = users
                    self.tableView.reloadData()
                }
            }, errorHandler: { error in
                switch EVCloudKitDao.handleCloudKitErrorAs(error, retryAttempt: retryCount) {
                case .Retry(let timeToWait):
                    Async.background(after: timeToWait) {
                        self.loadContacts(retryCount + 1)
                    }
                case .Fail:
                    Helper.showError("Something went wrong: \(error.localizedDescription)")
                default: // For here there is no need to handle the .Success, .Fail and .RecoverableError
                    break
                }
        })
    }
    

    在我上面的例子中,我使用了一个单独的错误回调处理程序。您也可以在 CloudKit 方法回调中直接调用它。只需先检查是否有错误。

    【讨论】:

      【解决方案3】:

      在 Swift 3 中,我以这种方式处理 CloudKit 错误:

      privateDB.perform(myQuery, inZoneWith: nil)  {records, error in
          if error != nil {
              print (error?.localizedDescription)
              if error?._code == CKError.notAuthenticated.rawValue {
                  // Solve problems here...
              }
      

      请注意,CKError.notAuthenticated 是从编写 CKError 后代码完成呈现的列表中选择的。

      希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-09
        • 2018-02-20
        • 2013-08-29
        • 2013-10-27
        • 2015-03-19
        • 1970-01-01
        • 1970-01-01
        • 2018-03-31
        相关资源
        最近更新 更多