首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >处理CloudKit错误

处理CloudKit错误
EN

Stack Overflow用户
提问于 2017-04-22 08:40:42
回答 2查看 3K关注 0票数 3

我正在寻找关于在Swift中处理CloudKit错误的一般建议,我在网上找不到好的例子。以下是我想知道的事情:

1)我是否应该在每次出现错误的可能性时都考虑到每一种错误类型,或者这不是真正必要的?

2)我了解到,处理CloudKit错误的一种常见方法是在错误消息提供的时间间隔后重新尝试执行操作。这个重试基本上应该是我对所有错误的标准程序吗?

3)执行不同的CloudKit操作(保存、获取等)产生不同类型的错误,还是只有一组标准的CloudKit错误?

提前感谢!我只是在寻找关于如何使用CloudKit处理错误处理的一般信息,因为我真的不确定从哪里开始。

EN

回答 2

Stack Overflow用户

发布于 2017-04-24 02:40:36

是的,您希望检查每个cloudkit调用是否有错误。Apple在与cloudkit相关的WWDC视频中强调了这一点。

当您检测到错误时,您所做的事情有很大的不同。重试有时是一个选项,但有时并不合适。如果您正在使用批处理操作,重试可能需要一些额外的工作来提取失败的记录。所以,是的,你有时可能想要重试,但不,你可能不会自动重试每个失败的操作。

有一组错误,在CKError.h中定义。但是,你并不总是能得到一个CKError。有时,尤其是使用CKErrorPartialFailure时,您会得到一个包含嵌套错误的顶级错误,您还必须对这些错误进行解包。从iOS10开始,CKError.h中的错误列表如下:

代码语言:javascript
复制
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操作是否有错误,如果检测到错误,则触发NSAssert来停止应用程序。然后,检查错误、潜在错误和上下文,以确定它失败的原因以及您需要对其采取的措施。随着时间的推移,您很可能会看到常见模式的出现,然后可以考虑构建一个通用的错误处理程序。

票数 9
EN

Stack Overflow用户

发布于 2017-04-22 09:22:04

我已经编写了一个CloudKit帮助,它使处理错误变得更容易。这只是一个起点,还可以做更多的事情。

在当前状态下,此帮助器的主要关注点是使重试错误变得容易,这些错误应该在适当的超时后重试。

但是您仍然需要处理不应该重试的错误,比如用户的iCloud存储已满。即使有了这个帮助器,每次对这些帮助器方法的调用都需要正确地处理结果,并可能向用户报告错误。当然,您可以添加一个帮助方法来检查所有可能的错误类型并显示适当的消息。然后,所有使用CloudKit代码的用户都可以调用该帮助器方法。

这也只涵盖了几个可能的操作。您可能还想添加对其他操作的支持。最后,这还不能处理部分错误。这将是另一个有用的增强。

代码语言:javascript
复制
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)
    }
}
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43553969

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档