我已经阅读了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)
给定应用程序报告的错误代码,现在很难分辨实际的错误情况。
其次,我希望这样的智能机制(特别是因为它是编译器生成的)也将 case 参数复制到userInfo
字典中 - 但它没有。
是我做错了,还是必须完全实现CustomNSError
协议才能获得有意义且一致的NSError
对象?当然,这是一个选项,但我希望它会自动完成(有点像Codable
的魔力)。
此外,应用程序是否可以获取错误案例名称作为String
?
作为参考,上面的片段是在 Xcode 10.3 Playground 中执行的。
简短回答:对于基于整数的枚举错误类型,错误值将按预期映射到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 中,对于枚举类型返回"标记",对于所有其他类型返回1
。例:
struct StringError: Error {}
let serr = StringError() as NSError
print(serr.code) // 1
枚举"标记"在"类型布局"文档中进行了描述。对于具有关联值的枚举,这不一定遵循声明案例的顺序。这就是为什么您观察到"意外"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]