如何正确地将带有参数的Swift错误传递到NSError中

7
我已经阅读了Swift Evolution关于改进桥接的文章以及其他一些在线资源,但仍然有些缺失。
考虑到这个自定义的Error枚举:
    public enum MyNetworkError: Error {
        case networkOffline
        case httpError(status: Int)
        case unknown
        case systemError(errno: Int)
    }

一段Objective-C应用程序需要能够读取错误对象,并从中提取错误名称(networkOffline、httpError、unknown、systemError)和案例参数(httpError.status和systemError.errno)。
将上述内容转换为NSError的结果令人惊讶,我正在尝试理解如何改进。
    let nse_A = MyNetworkError.networkOffline as NSError
    let nse_B = MyNetworkError.httpError(status: 503) as NSError
    let nse_C = MyNetworkError.unknown as NSError
    let nse_D = MyNetworkError.systemError(42) as NSError

首先,生成的错误代码。似乎具有参数的情况(无论其顺序如何)都会从零开始获得code
    print(nse_A.code)  // 2 (expected: 0)
    print(nse_B.code)  // 0 (expected: 1)
    print(nse_C.code)  // 3 (expected: 2)
    print(nse_D.code)  // 1 (expected: 3)

给定应用程序报告的错误代码,现在更难确定实际的错误情况。
其次,我期望这样一个智能机制(特别是它是编译器生成的)也会将案例参数复制到userInfo字典中 - 但它并没有这样做。
我是做错了吗,还是必须完全实现CustomNSError协议才能获得有意义和一致的NSError对象?当然,这是一个选择,但我希望它能自动完成(有点像Codable如何完成其魔术)。
此外,应用程序能否将错误案例名称作为String获取?
供参考,以上片段在Xcode 10.3 Playground中执行。
1个回答

16
简短回答:对于基于整数的枚举错误类型,错误值被映射到NSError代码中。对于所有其他错误类型(例如带有关联值的枚举),您必须实现CustomNSError协议才能控制NSError代码和用户信息。
一些细节:仅针对基于整数的错误类型,代码从Swift桥接到NSError,例如:
public enum IntNetworkError: Int, Error {
    case networkOffline = 13
    case httpError
    case unknown
    case systemError
}

let err = IntNetworkError.httpError as NSError
print(err.code) // 14

这是一个在ErrorType.swift中实现的特殊情况。对于所有其他错误类型,缺省实现在ErrorDefaultImpls.cpp中,并返回枚举类型的“tag”,对于所有其他类型,则返回1。例如:
struct StringError: Error {}

let serr = StringError() as  NSError
print(serr.code) // 1

“tag” 枚举类型在 Type Layout 文档中有描述。对于带有关联值的枚举类型,其顺序不一定遵循声明的顺序。这就是为什么你会观察到“意外”的 NSError 代码。
因此,正确的方法是实现 CustomNSError 协议,编译器不会为你合成它。
extension MyNetworkError: CustomNSError {

    public static var errorDomain: String {
        return "MyNetworkError"
    }

    public var errorCode: Int {
        switch self {
        case .networkOffline: return 1
        case .httpError: return 2
        case .unknown: return 3
        case .systemError: return 4
        }
    }

    public var errorUserInfo: [String : Any] {
        switch self {
        case .httpError(let status):
            return [ "status": status]
        case .systemError(let errno):
            return [ "errno": errno]
        default:
            return [:]
        }
    }
}

let nse_B = MyNetworkError.httpError(status: 503) as NSError

print(nse_B.code) // 2
print(nse_B.userInfo) // ["status": 503]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接