Swiftのエラー処理とは?エラーハンドリングの書き方を初心者向けに解説
プログラムは、ファイルが見つからない・通信が失敗する・入力が想定外だったなど、うまくいかない状況に必ず遭遇します。それを想定して書くのがエラー処理(エラーハンドリング)です。
この記事では、Swiftのエラー処理の書き方を、サンプルコードつきで解説します。
Swiftのエラー処理の全体像
Swiftのエラー処理は、次の4つの要素で構成されます。
| 要素 | 役割 |
|---|---|
Errorプロトコル |
エラーの型を定義する |
throws |
エラーを投げる可能性がある関数に付ける |
throw |
実際にエラーを投げる |
do-catch |
エラーを受け止める |
PythonやJavaScriptの例外処理と考え方は似ていますが、Swiftは「この関数はエラーを出す可能性がある」ことを型で明示する点が異なります。
エラーを定義する
まず、どんなエラーがあり得るかを列挙型で定義します。
enum InputError: Error {
case empty
case tooLong(max: Int)
case invalidCharacter
}
Error プロトコルに準拠させるのが決まりです。tooLong(max: Int) のように、エラーに情報を持たせることもできます。
エラーを投げる
エラーを出す可能性がある関数には throws を付けます。
func validate(_ name: String) throws -> String {
if name.isEmpty {
throw InputError.empty
}
if name.count > 10 {
throw InputError.tooLong(max: 10)
}
return name
}
throws が付いた関数は、呼び出す側で必ずエラー処理を書く必要があります。書かなければコンパイルエラーになります。
この仕組みにより、エラー処理の書き忘れを防げます。
エラーを受け止める:do-catch
do {
let name = try validate("Taro")
print("OK: \(name)")
} catch InputError.empty {
print("名前が入力されていません")
} catch InputError.tooLong(let max) {
print("\(max)文字以内で入力してください")
} catch {
print("その他のエラー: \(error)")
}
ポイントは3つです。
- エラーを出す可能性のある呼び出しには
tryを付ける catchはエラーの種類ごとに分けて書ける- 最後の
catch(条件なし)ですべてを受け止める
catch InputError.tooLong(let max) のように書くと、エラーが持っている値を取り出せます。
try の3つの書き方
| 書き方 | 動作 | 使いどころ |
|---|---|---|
try |
do-catchで処理する | 基本形 |
try? |
失敗したら nil を返す | 失敗しても構わない場合 |
try! |
失敗したらアプリが停止 | 原則避ける |
// try? : オプショナルになる
let name = try? validate("")
print(name ?? "入力エラー") // nil なので「入力エラー」
// try! : 失敗するとクラッシュする(危険)
// let name2 = try! validate("")
try! は絶対に失敗しないと確信できる場合以外は使わないでください。失敗した瞬間にアプリが落ちます。
guard と組み合わせる
条件を満たさないときに早めに関数を抜ける書き方です。ネストが深くなるのを防げます。
func register(_ name: String) {
guard !name.isEmpty else {
print("名前を入力してください")
return
}
guard name.count <= 10 else {
print("10文字以内にしてください")
return
}
print("登録しました: \(name)")
}
guard は「条件を満たさなければ抜ける」という書き方です。if でネストを重ねるより読みやすくなります。
エラーメッセージを分かりやすくする
LocalizedError に準拠させると、エラーに説明文を持たせられます。
enum InputError: LocalizedError {
case empty
case tooLong(max: Int)
var errorDescription: String? {
switch self {
case .empty:
return "名前が入力されていません"
case .tooLong(let max):
return "\(max)文字以内で入力してください"
}
}
}
do {
_ = try validate("")
} catch {
print(error.localizedDescription) // 名前が入力されていません
}
画面にメッセージを表示する場合、この形にしておくと扱いやすくなります。
SwiftUIでエラーメッセージを表示する
struct FormView: View {
@State private var name = ""
@State private var message: String?
var body: some View {
VStack {
TextField("名前", text: $name)
Button("登録") {
do {
_ = try validate(name)
message = nil
} catch {
message = error.localizedDescription
}
}
if let m = message {
Text(m).foregroundColor(.red)
}
}
.padding()
}
}
エラーメッセージを @State の変数に入れておき、値があるときだけ表示する、という形が基本です。
オプショナルとエラーの使い分け
Swiftには「値がないかもしれない」を表すオプショナルと、「処理が失敗した」を表すエラーの2つがあります。
| 状況 | 使うもの |
|---|---|
| 値がないことが普通にあり得る | オプショナル(String?) |
| 失敗の理由を伝えたい | エラー(throws) |
たとえば「辞書にキーがない」はオプショナル、「入力値が不正」はエラー、という使い分けが自然です。
まとめ
- エラーは
Errorプロトコルに準拠した列挙型で定義する - エラーを出す関数には
throws、投げるときはthrow - 受け止めるのは
do-catch。呼び出しにはtryが必要 try?は失敗時に nil、try!は失敗時にクラッシュするため原則避けるguardを使うとネストを浅く保てるLocalizedErrorに準拠させると説明文を持たせられる
Swiftの基本文法はSwiftの使い方|基本文法とサンプルコードで解説しています。







