Converting ErrorType to NSError loses associated objects Converting ErrorType to NSError loses associated objects swift swift

Converting ErrorType to NSError loses associated objects


New in 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]        }    }}


An ErrorType can't really be casted to an NSError, you have to take the associated data and package it into an NSError yourself.

do {    try haveAffairWith(otherPerson)} catch LifeError.GetCaughtByWife(let wife) {    throw NSError(domain:LifeErrorDomain code:-1 userInfo:        [NSLocalizedDescriptionKey:"You cheated on \(wife)")}

EDIT: Actually you can do the cast from ErrorType to NSError, but the NSError you get from the default implementation is quite primitive. What I'm doing in my app is hooking application:willPresentError: in my app delegate and using a custom class to read the my app's ErrorType's and decorate NSErrors to return.


Creating an NSError in every catch block can lead to a lot of copy and paste to convert your custom ErrorType to NSError. I abstracted it away similar to @powertoold.

protocol CustomErrorConvertible {    func userInfo() -> Dictionary<String,String>?    func errorDomain() -> String    func errorCode() -> Int}

This extension can hold code, that's common for the LifeError we already have and other custom error types we may create.

extension CustomErrorConvertible {    func error() -> NSError {        return NSError(domain: self.errorDomain(), code: self.errorCode(), userInfo: self.userInfo())    }}

Off to the implementation!

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        }    }}

And this is how to use it.

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)}

You could easily move certain functions like func userInfo() -> Dictionary<String,String>? from LifeError to extension CustomErrorConvertible or a different extension.

Instead of hardcoding the error codes like above an enum might be preferable.

enum LifeError:Int {  case Born  case LostJob}