CloudKit CKError 扩展在 Objective-C 中不可用?

CloudKit CKError extension not available in Objective-C?

我在这里的某个地方读到 CKError 在 Objective-C 中不可用,我同意。例如,此扩展程序在 Swift 中可用。

@available(OSX 10.10, iOS 8.0, watchOS 3.0, *)
extension CKError {

    /// Retrieve partial error results associated by item ID.
    public var partialErrorsByItemID: [AnyHashable : Error]? { get }

    /// The original CKRecord object that you used as the basis for
    /// making your changes.
    public var ancestorRecord: CKRecord? { get }

    /// The CKRecord object that was found on the server. Use this
    /// record as the basis for merging your changes.
    public var serverRecord: CKRecord? { get }

    /// The CKRecord object that you tried to save. This record is based
    /// on the record in the CKRecordChangedErrorAncestorRecordKey key
    /// but contains the additional changes you made.
    public var clientRecord: CKRecord? { get }

    /// The number of seconds after which you may retry a request. This
    /// key may be included in an error of type
    /// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
    public var retryAfterSeconds: Double? { get }
}

问题是我的 Objective-C 项目中需要这些对象。 我以某种方式(我相信)通过为 NSError 创建一个类别并稍微理解 CKError.h 的文档,设法在 Objective-C 中获得了 partialErrorsByItemID,如下所示:

CKErrorCode ckErrorCode = (CKErrorCode) _code;
if (ckErrorCode == CKErrorPartialFailure) {
    // When a CKErrorPartialFailure happens this key will be set in the error's userInfo dictionary.
    // The value of this key will be a dictionary, and the values will be errors for individual items with the keys being the item IDs that failed.

    NSDictionary *dicError = _userInfo;

    if ([dicError objectForKey:CKPartialErrorsByItemIDKey] != nil) {

        NSDictionary *dic = (NSDictionary *)[dicError objectForKey:CKPartialErrorsByItemIDKey];

        for (NSString* key in dic) {
            NSError *newError = dic[key];
            if (code == newError.code) {
                match = YES;
            }
        }

    } else {
        return NO;
    }
}

但同样,我的问题是如何获取对象 serverRecordclientRecord。有什么想法吗?

这是一个 Objective-C 类别,它复制了 Swift 的大部分 CKError 结构。我没有添加 errorCodelocalizedDescriptionerrorUserInfo,因为 NSError 已经提供了 codelocalizedDescriptionuserInfo .

CloudKitExtensions.h

#import <CloudKit/CloudKit.h>

NS_ASSUME_NONNULL_BEGIN

extern const double UnknownRetrySeconds;

@interface NSError (CKError)

- (NSDictionary<id, NSError *> * _Nullable)partialErrorsByItemID;
- (CKRecord * _Nullable)ancestorRecord;
- (CKRecord * _Nullable)clientRecord;
- (CKRecord * _Nullable)serverRecord;
- (double)retryAfterSeconds; // returns UnknownRetrySeconds if not available

@end

NS_ASSUME_NONNULL_END

CloudKitExtensions.m

#import "CloudKitExtensions.h"

const double UnknownRetrySeconds = -1;

@implementation NSError (CKError)

- (NSDictionary<id, NSError *> * _Nullable)partialErrorsByItemID {
    if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorPartialFailure) {
        return self.userInfo[CKPartialErrorsByItemIDKey];
    } else {
        return nil;
    }
}

- (CKRecord * _Nullable)ancestorRecord {
    if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
        return self.userInfo[CKRecordChangedErrorAncestorRecordKey];
    } else {
        return nil;
    }
}

- (CKRecord * _Nullable)clientRecord {
    if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
        return self.userInfo[CKRecordChangedErrorClientRecordKey];
    } else {
        return nil;
    }
}

- (CKRecord * _Nullable)serverRecord {
    if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
        return self.userInfo[CKRecordChangedErrorServerRecordKey];
    } else {
        return nil;
    }
}

- (double)retryAfterSeconds {
    if ([self.domain isEqualToString:CKErrorDomain]) {
        NSNumber *delayVal = self.userInfo[CKErrorRetryAfterKey];
        return delayVal ? [delayVal doubleValue] : UnknownRetrySeconds;
    } else {
        return UnknownRetrySeconds;
    }
}

@end