如何正确桥接 Swift 带有参数的错误到 NSError
How to correctly bridge Swift Errors with arguments to NSError
我已经阅读了 Swift Evolution post on the improved bridging 和其他一些在线资源,但仍然缺少一些内容。
鉴于此自定义 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. For all other error types, the default implementation is in ErrorDefaultImpls.cpp 中实现的特殊情况,returns 枚举类型的“标签”,以及所有其他类型的 1
。示例:
struct StringError: Error {}
let serr = StringError() as NSError
print(serr.code) // 1
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]
我已经阅读了 Swift Evolution post on the improved bridging 和其他一些在线资源,但仍然缺少一些内容。
鉴于此自定义 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. For all other error types, the default implementation is in ErrorDefaultImpls.cpp 中实现的特殊情况,returns 枚举类型的“标签”,以及所有其他类型的 1
。示例:
struct StringError: Error {}
let serr = StringError() as NSError
print(serr.code) // 1
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]