Do-Catchは、throwによってスローされた例外をキャッチし、対応するコードブロックを実行するSwift言語のエラー処理構文です。他の言語の従来のtry-catchとは異なり、Swiftではthrows修飾子を使用してエラーをスローできる関数を明示的にマークする必要があります。Swift.org、2026によると、Do-CatchはSwiftの主要なエラー処理メカニズムであり、型安全性を提供します。
重要ポイント
Do-Catchは、エラーをスローできるコードを実行するdoブロックと、そのエラーを処理する1つ以上のcatchブロックで構成されるSwiftの構文です。doブロックにはtryでマークされたthrowing関数の呼び出しが含まれ、catchブロックは型によってエラーをマッチングします。
SwiftはObjective-Cの例外とは異なる構造化エラー処理のモデルを実装しています。ここでのエラーはスタック巻き戻しを伴う例外ではなく、Errorプロトコルに準拠する値です。throwによるエラーのスローは、スタック巻き戻しのオーバーヘッドなしに最も近いcatchブロックに制御を移します。
Swiftの重要な特徴は明示的な処理です。コンパイラはtry、do-catch、または代替メカニズム(try?、try!)なしでthrowing関数の呼び出しを許可しません。これにより、エラーが見逃される状況が排除されます。
Swiftのエラー処理の中心にあるのはErrorプロトコルです。このプロトコルに準拠する任意の型はthrowによってスローできます。エラーは通常、Errorに準拠するenumとして定義されます。
Errorは空の(マーカー)プロトコルです。メソッドの実装は必要なく、準拠のみが必要です。Swiftコンパイラは静的なチェックにErrorを使用します:throwsを持つ関数はErrorに準拠する値のみをスローできます。
throwsでマークされた関数はtryとともに呼び出す必要があります。そのような関数内でthrowが発生すると、制御は呼び出し元のdo-catchに移ります。呼び出しチェーンのどこでもエラーが処理されない場合、エラーは上位レベルに伝播します。
enum FileError: Error {
case notFound
case permissionDenied
case corrupted(String)
}
func readFile(path: String) throws -> String {
guard FileManager.default.fileExists(atPath: path) else {
throw FileError.notFound
}
return try String(contentsOfFile: path)
}
rethrowsは、パラメータとしてthrowingクロージャを受け取る関数のための修飾子です。渡されたクロージャがエラーをスローしない場合、rethrows関数はtryなしで呼び出すことができます。これにより、throwingとnon-throwingの両方のクロージャと互換性のあるジェネリック高階関数を書くことができます。
Swiftは型ベースのエラーマッチングによる複数のcatchブロックをサポートしています。これはKotlinのwhenや型によるswitchと似ています:最初にマッチしたcatchが実行され、残りはスキップされます。
各catchブロックは特定のエラー型にマッチするパターンを含むことができます。Swiftはブロックを順番にチェックするため、より具体的な型は一般的な型の前に配置する必要があります。どのパターンもマッチしない場合、パターンのないcatchブロックが実行されます。
Do-Catchの型安全性により、各エラーを個別に処理したり、カンマ区切りのパターンマッチングを通じて1つのブロックで複数の型を組み合わせたりできます。これにより、Objective-Cに典型的だったif-elseチェーンが不要になります。Objective-Cでは、戻りコードまたはドメインとコードのチェックによるNSErrorを通じてエラー分析が行われていました。
Swiftは追加条件によるマッチングのためのcatch whereもサポートしています。たとえば、特定のコードやテキストでエラーをフィルタリングできます:catch URLError where error.code == .notConnectedToInternet。これにより、精度を維持しながらcatchブロックの数を減らせます。
throwing関数のデバッグのために、Swiftはエラーと例外の区別を提供します。Swiftのエラーはスタック巻き戻しを必要としない戻り値です。Objective-Cの例外(@try/@catch)はランタイムレベルで動作し、致命的な障害にのみ使用されます。Do-Catchは例外ではなくエラーを処理するため、予測可能で効率的です。
ライブラリやSDKを開発する際、throwing関数は公開契約の一部であることを覚えておくことが重要です。エラー型はシグネチャで指定されていないため、関数がスローする可能性のあるエラーをコメントやResult風のenumを通じて文書化してください。これにより、APIユーザーは実装を見ることなく正しいcatchブロックを書くことができます。
非同期プログラミングの文脈では、async/awaitを使用したDo-Catchがコールバック地獄の問題を解決します。以前のSwiftでは、非同期コードでのエラー処理には、completionハンドラーでのError?チェックを伴うネストされたクロージャが必要でした。async/awaitとDo-Catchを使用すると、非同期throwingコードは同期コードのように見え、各ステップでのエラー処理を含む複雑なリクエストチェーンの読み取りと保守が簡素化されます。
do {
let content = try readFile(path: "/data/config.json")
process(content)
} catch FileError.notFound {
createDefaultConfig()
} catch FileError.permissionDenied {
requestAccess()
} catch FileError.corrupted(let detail) {
logCorruption(detail)
} catch {
print("不明なエラー: \(error)")
}
Swiftはthrowing関数を呼び出す3つの方法を提供します:try、try?、try!。各オプションは特定のタスクを解決し、制限があります。
tryはdoブロック内で使用され、エラーはcatchで処理されます。これがthrowing関数を呼び出す主要な方法です。コンパイラはtryがエラーをキャッチできるコンテキストにあることを要求します。
try?はthrowing関数の結果をOptionalに変換します。関数がエラーをスローした場合、try?はnilを返し、そうでなければ成功した値を持つOptionalを返します。エラーを無視できるが成功の指標が必要な呼び出しに便利です。
try!はエラー処理を抑制します。throwing関数がエラーをスローした場合、アプリはランタイムエラーで終了します。try!は、エラーが絶対に不可能であると確信できる場合にのみ使用してください — たとえば、バンドルリソースの読み込み時など。
// try? — エラーを無視、失敗時にnilを取得
if let data = try? Data(contentsOf: url) {
processData(data)
}
// try! — エラーが絶対に不可能な場合のみ
let bundled = try! String(contentsOfFile: "Assets/default.txt")
// try — do-catchの標準オプション
do {
let result = try performNetworkRequest()
updateUI(result)
} catch {
showError(error)
}
Do-Catchは、システムAPIを扱うiOS開発で積極的に使用されています。典型的なシナリオを見てみましょう:ファイルシステム、Core Data、ネットワークリクエスト。
FileManagerの多くのメソッドはthrowingです。Do-Catchにより、ファイルの不存在、リソースのビジー状態、権限不足を適切に処理できます。
JSONDecoderによるJSON解析はthrowing操作です。Do-Catchはデコードエラーとネットワーク障害を個別にキャッチし、ユーザーに正確なエラーメッセージを提供します。
struct User: Codable {
let id: Int
let name: String
}
func fetchUser(id: Int) async throws -> User {
let url = URL(string: "https://api.example.com/users/\(id)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
// 処理付き呼び出し
do {
let user = try await fetchUser(id: 42)
showUser(user)
} catch let error as URLError {
showNetworkAlert(error)
} catch let error as DecodingError {
showParseError(error)
} catch {
showGenericError(error)
}
特に他の言語から移行してきた開発者は、Do-Catchを使用する際に特徴的な間違いをよく犯します。最も一般的なものを見てみましょう。
よくある質問
Swiftでは、エラーはErrorプロトコルに準拠する値であり、スタック巻き戻しを伴う例外ではありません。throwing関数は明示的にthrowsでマークする必要があり、コンパイラは呼び出し時にtryを要求します — これによりコンパイル時に未処理のエラーが排除されます。
はい、async関数もthrowsにでき、do-catchはそれらで機能します。非同期throwing関数の呼び出しには、doブロック内でtry awaitが必要です。これは非同期Swiftコードでエラーを処理する標準的な方法です。
代替手段があります:try?はエラーをnilに変換し、try!はエラー時にクラッシュを引き起こし、rethrowsはクロージャからエラーを伝播できます。do-catchは明示的な処理の主要な方法であり続けます。
Swiftはスローされるエラーを型付けしません — 任意のthrowing関数はErrorに準拠する任意の型をスローできます。特定のエラー型によるcatchでのマッチングを通じて処理を分けてください。
はい、それはベストプラクティスです。Errorに準拠するenumとしてAppErrorを定義し、システムエラーをドメインエラーに変換してください。これにより処理が統一され、アプリケーション層がシステムの詳細から分離されます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。