Errorプロトコルの使い方

2021年03月09日

SwiftでAPIにアクセスした時や何か処理に失敗した時に Errorプロトコル に準拠したenumやstructをよく用います

Errorに準拠した型の定義方法

例えば以下のように定義をします

enum AppError: Error {
    case network
    case api(Int) //数値を渡す
    case auth
    case unkown(String) //文字列を渡す
}

エラーの投げ方

メソッドには引数の後に throws キーワードをつけ、実際にエラーを投げる際には throw 文で投げます

func throwErrorSample() throws -> Void {
    let hasNetworkError = false
    let hasUnownError = true

    if hasNetworkError {
        throw AppError.network
    }

    if hasUnownError {
        throw AppError.unkown("不明のエラー")
    }
}

tryの使い方

エラーを持つメソッドに対して try を用いる事が出来ます。
trytry!try? の3つの書き方 があります。

do-catchとtry

他言語のtry-catchと近い書き方が出来ます

do {
    try throwErrorSample()
} catch APIError.network {
    // .network のエラー場合
} catch APIError.unknown(let message) {
    // .unknown のエラー場合
    print(message)
    // => "不明のエラー"
} catch {
    // それ以外のエラー場合
}

try? と try!

do-catchを用いないSwiftらしい書き方が出来ます
参考に引数で渡したBool値を元にErrorを返すメソッドを使ってみます

struct User {
    let uid: String
    let name: String
}

/// 引数の値がtrueだった場合強制的に AppError.api を返す
func loadUserInfo(hasApiError: Bool) throws -> User {
    if hasApiError {
        throw AppError.api(404)
    }
    
    let user = User(uid: "uid-sample", name: "taro")
    return user
}

try?

Errorがない場合はOptionalの値を、Errorの場合はnilを返します。
そのため guard や if let と相性良く使うことが出来ます

// Errorが返らない場合

let user = try? loadUserInfo(hasApiError: false)
print(user)
// => Optional(User(uid: "uid-sample", name: "taro"))


/* guardと合わせて使う */
guard let user = try? loadUserInfo(hasApiError: false) else { return }
print(user)
// => User(uid: "uid-sample", name: "taro")
// Errorが返る場合

let user = try? loadUserInfo(hasApiError: true)
print(user)
// => nil

try!

Errorがない場合は値を返し、Errorの場合はクラッシュします。

// Errorが返らない場合

let user = try! loadUserInfo(hasApiError: false)
print(user)
// => User(uid: "uid-sample", name: "taro")
// Errorが返る場合

let user = try! loadUserInfo(hasApiError: true)
// => Fatal error: 'try!' expression unexpectedly raised an error: SwiftErrorSample.AppError.api(404)

おすすめ