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}