在Swift 2.0 NSError
中符合ErrorType
协议。
对于自定义错误,我们可以为某些情况指定关联对象,如下所示。
enum LifeError: ErrorType {
case BeBorn
case LostJob(job: String)
case GetCaughtByWife(wife: String)
...
}
我们可以轻松地做到以下几点:
do {
try haveAffairWith(otherPerson)
} catch LifeError.GetCaughtByWife(let wife) {
...
}
但是,如果我们希望它作为NSError
传递到其他地方,则会丢失其关联对象信息。
println("\(LifeError.GetCaughtByWife("Name") as NSError)")
打印:
Error Domain=... Code=1 "The operation couldn't be completed". (... error 1)
及其userInfo
为nil
。
我的wife
与ErrorType
?
答案 0 :(得分:42)
Xcode 8新功能:CustomNSError
protocol。
enum LifeError: CustomNSError {
case beBorn
case lostJob(job: String)
case getCaughtByWife(wife: String)
static var errorDomain: String {
return "LifeError"
}
var errorCode: Int {
switch self {
case .beBorn:
return 0
case .lostJob(_):
return 1
case .getCaughtByWife(_):
return 2
}
}
var errorUserInfo: [String : AnyObject] {
switch self {
case .beBorn:
return [:]
case .lostJob(let job):
return ["Job": job]
case .getCaughtByWife(let wife):
return ["Wife": wife]
}
}
}
答案 1 :(得分:23)
ErrorType
无法真正投放到NSError
,您必须自行获取相关数据并将其打包成NSError
。
do {
try haveAffairWith(otherPerson)
} catch LifeError.GetCaughtByWife(let wife) {
throw NSError(domain:LifeErrorDomain code:-1 userInfo:
[NSLocalizedDescriptionKey:"You cheated on \(wife)")
}
编辑:实际上你可以从ErrorType
到NSError
进行演员表,但你从默认实现中获得的NSError
非常原始。我在我的应用程序中正在做的是挂钩应用程序:willPresentError:在我的应用程序委托中并使用自定义类来读取我的应用程序的ErrorType
并装饰NSErrors以返回。
答案 2 :(得分:14)
在每个catch块中创建NSError
可能会导致大量复制和粘贴,以将自定义ErrorType
转换为NSError
。我把它抽象出类似于@powertoold。
protocol CustomErrorConvertible {
func userInfo() -> Dictionary<String,String>?
func errorDomain() -> String
func errorCode() -> Int
}
此扩展程序可以包含代码,这对于我们已经拥有的LifeError
以及我们可能创建的其他自定义错误类型而言很常见。
extension CustomErrorConvertible {
func error() -> NSError {
return NSError(domain: self.errorDomain(), code: self.errorCode(), userInfo: self.userInfo())
}
}
关闭实施!
enum LifeError: ErrorType, CustomErrorConvertible {
case BeBorn
case LostJob(job: String)
case GetCaughtByPolice(police: String)
func errorDomain() -> String {
return "LifeErrorDomain"
}
func userInfo() -> Dictionary<String,String>? {
var userInfo:Dictionary<String,String>?
if let errorString = errorDescription() {
userInfo = [NSLocalizedDescriptionKey: errorString]
}
return userInfo
}
func errorDescription() -> String? {
var errorString:String?
switch self {
case .LostJob(let job):
errorString = "fired as " + job
case .GetCaughtByPolice(let cops):
errorString = "arrested by " + cops
default:
break;
}
return errorString
}
func errorCode() -> Int {
switch self {
case .BeBorn:
return 1
case .LostJob(_):
return -9000
case .GetCaughtByPolice(_):
return 50
}
}
}
这就是如何使用它。
func lifeErrorThrow() throws {
throw LifeError.LostJob(job: "L33tHax0r")
}
do {
try lifeErrorThrow()
}
catch LifeError.BeBorn {
print("vala morgulis")
}
catch let myerr as LifeError {
let error = myerr.error()
print(error)
}
您可以轻松地将func userInfo() -> Dictionary<String,String>?
等某些功能从LifeError
移至extension CustomErrorConvertible
或不同的扩展程序。
不是像上面那样对错误代码进行硬编码,而是枚举可能更好。
enum LifeError:Int {
case Born
case LostJob
}
答案 3 :(得分:6)
我对此问题的解决方案是创建一个符合Int,ErrorType:
的枚举enum AppError: Int, ErrorType {
case UserNotLoggedIn
case InternetUnavailable
}
然后扩展枚举以符合CustomStringConvertible和一个名为CustomErrorConvertible的自定义协议:
extension AppError: CustomStringConvertible, CustomErrorConvertible
protocol CustomErrorConvertible {
var error: NSError { get }
}
对于描述和错误,我打开了AppError。例如:
Description: switch self {
case .UserNotLoggedIn: return NSLocalizedString("ErrorUserNotLoggedIn", comment: "User not logged into cloud account.")
case .InternetUnavailable: return NSLocalizedString("ErrorInternetUnavailable", comment: "Internet connection not available.")
}
Error: switch self {
case .UserNotLoggedIn: errorCode = UserNotLoggedIn.rawValue; errorDescription = UserNotLoggedIn.description
case .InternetUnavailable: errorCode = InternetUnavailable.rawValue; errorDescription = InternetUnavailable.description
}
然后我编写了自己的NSError:
return NSError(domain:NSBundle.mainBundle().bundleIdentifier!, code:errorCode, userInfo:[NSLocalizedDescriptionKey: errorDescription])
答案 4 :(得分:2)
我也使用PromiseKit解决了这个问题,我找到了一个可能有点难看但似乎有用的解决方法。
我在这里粘贴我的游乐场,这样你就可以看到整个过程。
import Foundation
import PromiseKit
import XCPlayground
let error = NSError(domain: "a", code: 1, userInfo: ["hello":"hello"])
// Only casting won't lose the user info
let castedError = error as ErrorType
let stillHaveUserInfo = castedError as NSError
// when using promises
func convert(error: ErrorType) -> Promise<Int> {
return Promise<Int> {
(fulfill, reject) in
reject(error)
}
}
let promiseA = convert(error)
// Seems to lose the user info once we cast back to NSError
promiseA.report { (promiseError) -> Void in
let lostUserInfo = promiseError as NSError
}
// Workaround
protocol CastingNSErrorHelper {
var userInfo: [NSObject : AnyObject] { get }
}
extension NSError : CastingNSErrorHelper {}
promiseA.report { (promiseError) -> Void in
let castingNSErrorHelper = promiseError as! CastingNSErrorHelper
let recoveredErrorWithUserInfo = castingNSErrorHelper as! NSError
}
XCPSetExecutionShouldContinueIndefinitely()
答案 5 :(得分:2)
我找到的最佳解决方案是使用Objective-C包装器将ErrorType
转换为NSError
(通过NSObject*
参数)并提取userInfo
。很可能这也适用于其他相关对象。
就我而言,仅使用Swift的所有其他尝试都会获得nil
userInfo
。
这是Objective-C帮助器。例如,将它放在暴露于Swift的MyErrorUtils
类中:
+ (NSDictionary*)getUserInfo:(NSObject *)error {
NSError *nsError = (NSError *)error;
if (nsError != nil) {
return [nsError userInfo];
} else {
return nil;
}
}
然后在Swift中使用帮助器:
static func myErrorHandler(error: ErrorType) {
// Note the as? cast to NSObject
if let userInfo: [NSObject: AnyObject]? =
MyErrorUtils.getUserInfo(error as? NSObject) {
let myUserInfo = userInfo["myCustomUserInfo"]
// ... Error processing based on userInfo ...
}
}
(我目前正在使用XCode 8和Swift 2.3)
答案 6 :(得分:1)
正如接受的答案所指出的,现在Swift 3中有CustomNSError
,但是,你不一定需要使用它。如果您定义错误类型
@objc
enum MyErrorType: Int, Error { ... }
然后可以将此错误直接投放到NSError
:
let error: MyErrorType = ...
let objcError = error as NSError
我今天刚刚发现,虽然我与世界分享。