In 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
に関連付けられていますか?
Xcode 8の新機能:CustomNSError
プロトコル 。
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]
}
}
}
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
は非常に原始的です。私のアプリで行っているのは、アプリのデリゲートでapplication:willPresentError:をフックし、カスタムクラスを使用してアプリのErrorType
を読み取り、NSErrorをデコレートして返すことです。
すべての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をお勧めします。
_enum LifeError:Int {
case Born
case LostJob
}
_
この問題に対する私の解決策は、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])
私が見つけた最良の解決策は、ErrorType
をNSError
に(NSObject*
parmeterを介して)キャストし、userInfo
を抽出するためのObjective-Cラッパーを持つことです。ほとんどの場合、これは他の関連オブジェクトでも機能します。
私の場合、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)を使用しています
私も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()
受け入れられた答えが指摘したように、今ではCustomNSError
in Swift 3)がありますが、必ずしもそれを使用する必要はありません。
@objc
enum MyErrorType: Int, Error { ... }
次に、このエラーをNSError
に直接キャストできます。
let error: MyErrorType = ...
let objcError = error as NSError
私はそれを今日発見しましたが、それを世界と共有しています。