🔴

【Swift】開発中、とりあえず適当なエラーを投げたい…!ときのアイディア4選

2023/12/11に公開

この記事は SwiftWednesday Advent Calendar 2023 の11日目の記事です。


Swift でエラーを扱うには、Error に準拠したカスタムタイプを用意する必要があります。

func someFunc() throws {
    // あとでちゃんと書くから、
    // 今はとりあえず適当なエラーを投げたい…!
}

「ちょっと今はエラーのタイプを用意しないで、適当に書きたい…!」「自分しか扱わない使い捨てのコードスニペットだから、エラーは適当に書いておいても大丈夫……!」と思ったとき、どのような表現ができるかまとめました。

自分以外の人が見るようなコード・プロダクトでは使わないことをおすすめします。

【おすすめ】LocalizedErrorString を準拠させる

私は基本的にこの方法を使っています。Error に準拠している LocalizedErrorString が準拠するよう、以下のコードをどこかに書いておきます。

import Foundation

#if !(RELEASE) // 意図せずリリースビルドで使用されることがないようにする
extension String: LocalizedError {
    public var errorDescription: String? { self }
}
#endif

すると、エラーを投げるべき場所において String を使えるようになります。

func someFunc() throws {
    guard /* ... */ else {
        throw "エラーだよ"
    }
    // ...
}

LocalizedError.errorDescriptionself を返すようにしたので、Error.localizedDescription でその文字列を簡単に得ることができます。

do {
    try someFunc()
} catch {
    print(error) // エラーだよ
    print(error.localizedDescription) // エラーだよ
}

【おすすめ】ErrorString を準拠させる

ErrorString が準拠するよう、以下のコードをどこかに書いておきます。

import Foundation

#if !(RELEASE) // 意図せずリリースビルドで使用されることがないようにする
extension String: Error {}
#endif

すると、エラーを投げるべき場所において String を使えるようになります。

func someFunc() throws {
    guard /* ... */ else {
        throw "エラーだよ"
    }
    // ...
}

エラーを print(_:separator:terminator:) などすると、その文字列を出力として得られます。

do {
    try someFunc()
} catch {
    print(error) // エラーだよ
    print(error.localizedDescription) // The operation couldn’t be completed. (Swift.String error 1.)
}

【要注意】Swift に入っている Error に準拠したタイプを投げる

Swift には元々 Error に準拠したタイプがいくつか存在するため、それを借りるアイディアです。

import Foundation

func someFunc() throws {
    guard /* ... */ else {
        throw CancellationError()
    }
    // ...
}

例えば CancellationError を投げてみました。

do {
    try someFunc()
} catch {
    print(error) // CancellationError()
    print(error.localizedDescription) // The operation couldn’t be completed. (Swift.CancellationError error 1.)
}

しかしこの方法は、本来投げられるべき場面が決まっているエラーたちを、自分で用意する必要がないからといって勝手に借りて使用するため、使いどころには要注意です。

【もう一工夫】そのままリリースビルドしたときに、コンパイルエラーメッセージをわかりやすくする

たとえば、先述の CancellationError を投げる例の場合、このままだとリリースビルドにおいてコンパイルエラーにならないため、CancellationError が使われていることを失念する可能性があります。

そのようなときは、error(_:) マクロを併せて記述しておくことで、ビルド設定がリリースに変わったときはコンパイル時エラーにする…… といったことが可能です。

#if DEBUG
throw CancellationError()
#else
#error("Implement me!")
#endif

【ObjC のちから】NSError を投げる

Objective-C 時代からの資産である NSError を投げるのも1つの方法です。

import Foundation

func someFunc() throws {
    guard /* ... */ false else {
        throw NSError(domain: "jp.tret.playground", code: 1)
    }
    // ...
}
do {
    try someFunc()
} catch {
    print(error) // Error Domain=jp.tret.playground Code=1 "(null)"
    print(error.localizedDescription) // The operation couldn’t be completed. (jp.tret.playground error 1.)
}

もし、この NSError のようにドメイン、エラーコードなどの情報を返すカスタムタイプを Swift で実装する場合は、CustomNSError に準拠したカスタムタイプを実装することをおすすめします。

本来ならば……

本来であれば、Error に準拠したカスタムタイプを用意し、適したエラーを投げることが必要です。エラーのためのカスタムタイプの作り方は Apple および Swift のドキュメントも参照してください。

DeNA Engineers

Discussion