はじめに
いつも、swiftで開発しているのですが、Error・NSErrorの2つのエラーオブジェクトが出てきて
いつも何で分けられてるの?と疑問に思いながらErrorからNSErrorに型キャストしたりしてました。
よく分からないままだと流石にまずいなと思い、Qiitaに備忘録として残そうと思った感じです。
ErrorとNSErrorの違いとは
そもそも、この2つの違いは何でしょうか?
調べてみると
・NSErrorはObjective-C
のエラーオブジェクトでクラス
として定義されている。
・Errorはswift
のエラーオブジェクトでプロトコル
として定義されている。
という感じでした。
更に、深く調べてみましょう。
NSError
NSErrorはObjective-C
で使われていたエラーオブジェクトでエラー状態に関する情報
が含まれています。
どんな情報が含まれているのかというと
domain
・code
・userInfo
という3つの情報が含まれています。
分かりやすようにまとめてみました↓
情報 | 意味 |
---|---|
domain | エラーの種類を識別するための文字列 |
code | エラーの種類を識別するための整数値 |
userInfo | エラーに関する付加情報 |
swiftのErrorはNSErrorに常にキャスト
できるようになっており、それらの情報を参照
することができます。
実際にコードを書いてみるとこんな感じです↓
func manager(error: Error) {
// ErrorからNSErrorにキャスト
let nsError = error as NSError
// 欲しい情報を参照する
print(nsError.domain)
print(nsError.code)
print(nsError.userInfo)
}
例えば何かのマネージャークラスのデリゲートメソッドがあって
エラーが発生したら呼び出されるメソッドがあるとします。
そしたら、そのエラーをNSError
にキャストして欲しい情報を参照
していくという流れですね。
NSErrorの使いどころとは
swiftではObjective-C
との互換性を保つために存在していますが、swiftのエラーハンドリングが強力なのもあって、NSErrorで使っていたdomainやcodeを使う機会は減ってしまったそうです。
ただ、2つの言語が混在しているプロジェクト
であればNSErrorを意識する場面に直面するそうですね。
私は、全くObjective-Cを触ったことがないので分かりませんが自分の開発しているアプリでは
domainやcodeの情報が欲しい時があるので割とキャストして使っています。
Error
最初の方で述べたようにswift
で使われているエラーオブジェクトでプロトコル
として定義されています。
そんなswiftのErrorは準拠した型がエラーを表現する型として扱えることを示すためのプロトコル
であって
準拠するために必要な実装はないです。
つまり、swiftのErrorはエラー情報を表現するプロトコル(規約)
なんですね。
因みに余談ですが、swift2ではErrorはErrorType
という名前だったそうで
swift3からError
という名前に変わったそうです。
Errorの使いどころとは
このErrorプロトコルに準拠する型は、列挙型(enum)
に準拠するのが一般的で
発生するエラーをまとめて記述できる
というメリットがあるからです。
更に、プログラム全体で起こり得るあらゆるエラーを一つの列挙型で定義せずに
エラーの種類によって別の型を定義する
のが常だそうですね。
では実際に、Errorプロトコルを準拠して列挙型で定義してみましょう。
今回は、API通信を行う時に発生するであろうエラーを列挙型で定義してみます。
enum ApiError: Error {
case networkError
case decodeFailed
case responceFailed
case unknown
}
このように定義することができます。
後は引数
を持たせて、このような使い方が出来ます。
以下の例では、computedプロパティを使って、ケースごとにメッセージを返すようにしています↓
enum ApiError: Error {
case networkError
case decodeFailed(Error)
case responceFailed(Error?)
case unknown
var message: String {
switch self {
case .networkError:
return "通信エラーが発生しました"
case .decodeFailed(let error):
return "デコードに失敗しました \(error.localizedDescription)"
case .responceFailed(let error):
return "レスポンスの取得に失敗しました \(error?.localizedDescription ?? "error")"
case .unknown:
return "不明なエラーが発生しました"
}
}
}
let networkErrorMessage = ApiError.networkError.message
let decodeErrorMessage = ApiError.decodeFailed(error).message
Errorからエラーメッセージを取得したい場合は、localizedDescriptionプロパティを使う方法があります。
このプロパティは元々、NSErrorが定義しているプロパティなので、swiftのErrorが持っているプロパティではないのですがFoundationフレームワークをリンクしていると使えるようになります。
なぜ使えるのかというと、そもそもFoundation
フレームワークはObjective-Cで作られており、Objective-CではFoundationフレームワークに含まれるNSErrorクラスを使ってエラーハンドリングしていたからです。
なので、swiftでもFoundationをインポートすればNSErrorクラスが使える
ということです。
他にもInt型
やString型
なども引数に持たせることが可能で
エラーに付随する情報
を表現することが出来ます。
enum DatabaseError {
case networkError
case invalidEntry(reson: String)
}
let invalidEntryReson = DatabaseError.invalidEntry(reson: "~が理由で無効です")
おわり
ErrorとNSErrorの違いをまとめてみました。
swiftしか触ったことがないので、かなりswift寄りの記事になってしまいましたね。
もし、間違っている部分があればコメントして下さると有り難いです。
最後に下記にて参考資料を載せておきます。
参考資料
Swift3時代のErrorとNSErrorに関するいくつかの実験
Swift 4.0 エラー処理入門
[Swift] Swiftのエラー処理についてざっくりとまとめてみた