LoginSignup
24
10

More than 3 years have passed since last update.

swiftのErrorとNSErrorの違いがよく分からないので自分なりにまとめてみた

Last updated at Posted at 2021-01-14

はじめに

いつも、swiftで開発しているのですが、Error・NSErrorの2つのエラーオブジェクトが出てきて
いつも何で分けられてるの?と疑問に思いながらErrorからNSErrorに型キャストしたりしてました。

よく分からないままだと流石にまずいなと思い、Qiitaに備忘録として残そうと思った感じです。

ErrorとNSErrorの違いとは

そもそも、この2つの違いは何でしょうか?

調べてみると

・NSErrorはObjective-Cのエラーオブジェクトでクラスとして定義されている。

・Errorはswiftのエラーオブジェクトでプロトコルとして定義されている。

という感じでした。

更に、深く調べてみましょう。

NSError

NSErrorObjective-Cで使われていたエラーオブジェクトでエラー状態に関する情報が含まれています。

どんな情報が含まれているのかというと
domaincodeuserInfoという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のエラー処理についてざっくりとまとめてみた

24
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
10