Swiftは、Errorプロトコルとthrow、try、catchの構文を通じて厳格なエラーハンドリングシステムをサポートしています。独自のエラータイプを定義することができ、Errorから継承できます:
enum NetworkError: Error { case noInternet case serverError(code: Int) case unknown }
エラーをスローする可能性のある関数はthrowsとして宣言されます:
func fetchData() throws -> Data { // ... throw NetworkError.noInternet }
エラーはdo-catchブロックを使って処理できます:
do { let data = try fetchData() // データを操作 } catch NetworkError.noInternet { print("インターネットがありません") } catch { print("別のエラー: \(error)") }
もう一つのアプローチは、Result<T, Error>タイプを使うことで、try-catchなしで結果またはエラーを返すことができます:
func fetchData() -> Result<Data, NetworkError> { // ... return .failure(.noInternet) } let result = fetchData() switch result { case .success(let data): // OK case .failure(let err): // エラー処理 }
いつ使うべきか:
try/catchを使用してください。Resultを使用してください。質問: 「文字列や数値など、任意のタイプのエラーを投げたり捕まえたりできますか?」
答え: いいえ、SwiftではErrorプロトコルに準拠したタイプのみをスローできます。
// 間違い: throw "StringError" // コンパイラはこれを許可しません // 正しい: struct MyError: Error {} throw MyError()
ストーリー
REST APIクライアントのプロジェクトで、エラーを文字列でスローしていました(throw "No data")。JavaScriptではコンパイルされましたが、Swiftに翻訳した際に致命的なコンパイルエラーが発生しました。
ストーリー
開発者はオプショナル値を介してエラーを返していました(エラー時にreturn nil)、throw/Resultを通じてではありませんでした。その結果、エラーに関する詳細が失われ、正しく処理することが難しくなり、Silent failsが発生しました。
ストーリー
分析の結果、アプリケーションのいくつかの部分で同じエラーが一つのErrorタイプにグループ化されていないことが判明しました。その結果、アプリケーションが同じ種類の失敗を異なる方法で処理し、UIが同じエラーに対して異なるメッセージを表示していました—保守とテストが困難でした。