Swiftでエラー処理をしよう!初心者でもわかる12のステップ – JPSM

Swiftでエラー処理をしよう!初心者でもわかる12のステップ

Swift言語のロゴとエラーマークのイラストが並んでいる画像Swift

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

このサービスは複数のSSPによる協力の下、運営されています。

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

また、理解しにくい説明や難しい問題に躓いても、JPSMがプログラミングの解説に特化してオリジナルにチューニングした画面右下のAIアシスタントに質問していだければ、特殊な問題でも指示に従い解決できるように作ってあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

皆さん、Swiftのコーディング中に何らかのエラーに直面したことはありませんか?

多くのプログラマーが一度は経験するエラーによって、どれだけの時間を無駄にしたことか…。

しかし、エラー処理をしっかりと理解し、正しく実装すれば、そうした悩みはぐっと減少します。

この記事を読めば、Swiftでのエラー処理の基礎から、具体的な使い方、応用例までしっかりと把握することができるようになります。

Swiftでのエラー処理を学ぶことは、あなたのコーディングスキルを一段階上げるための大切なステップです。

●Swiftのエラー処理とは

Swiftのエラー処理は、プログラムの中で発生する予期しない状況や不正な操作を検出し、それに適切に対処するための方法を提供しています。

具体的には、エラーが発生する可能性のある箇所での処理の流れを制御し、問題が発生した場合には適切な対応を取ることができます。

Swiftのエラー処理の背景には、アプリケーションの安定性と信頼性を高めるための思想があります。

エラーを無視して放置すると、アプリケーションのクラッシュの原因となることが多いため、Swiftではエラー処理を重視しています。

○エラー処理の基本概念

Swiftのエラー処理は主に「エラーのスロー」と「エラーのキャッチ」の2つのステップで構成されています。

  1. エラーのスロー:関数やメソッドがエラー状態を検出したときにエラーを発生(スロー)します。このとき、throwキーワードを使用します。
  2. エラーのキャッチ:エラーがスローされたとき、それを検出して適切な処理を行います。do-catch文を使用してエラーを捕捉(キャッチ)します。

この2つのステップを適切に組み合わせることで、Swiftのエラー処理は機能します。

これに加えて、エラーの種類を表すための「エラー型」という概念もあります。

エラー型は、発生したエラーの内容や原因を表すためのもので、Swiftでは列挙型を使って表現されます。

●Swiftのエラー処理の使い方

Swiftでのエラー処理の使い方を学ぶことは、安定したアプリケーションの実装の鍵となります。

エラー処理を適切に実装することで、アプリケーションはより頑丈になり、ユーザーに安心して使ってもらえるようになります。

では、実際にどのようにSwiftでエラー処理を行うのか、具体的なサンプルコードと共に見ていきましょう。

○サンプルコード1:エラー型の定義

エラー処理の最初のステップは、エラーの原因や種類を表すエラー型を定義することです。

Swiftでは、エラー型は列挙型で表現されます。

enum NetworkError: Error {
    case invalidURL
    case timeout
    case unknown
}

このコードでは、NetworkErrorというエラー型を定義しています。

このエラー型は、ネットワーク関連のエラーを表しており、URLが無効である場合、タイムアウトした場合、そして原因不明のエラーの3つのケースを持っています。

○サンプルコード2:関数内でのエラー発生

エラー型を定義したら、関数やメソッド内でエラーを発生させることができます。

これを「エラーをスローする」と言います。

func fetchData(from url: String) throws {
    if url == "" {
        throw NetworkError.invalidURL
    }
    // データの取得処理...
}

この関数fetchDataは、与えられたURLからデータを取得するという機能を持っています。

しかし、URLが空の場合はNetworkError.invalidURLエラーをスローしています。

このように、関数の中で条件によってエラーをスローすることができます。

このコードを実行すると、urlが空文字列の場合、NetworkError.invalidURLエラーが発生します。

このエラーを呼び出し元でキャッチするための手法が、次に紹介する「トライ&キャッチ」になります。

○サンプルコード3:トライ&キャッチの基本

Swiftではエラーをスローする際にはtryキーワードを用い、エラーを捕捉する際にはcatchブロックを使用します。

このtrycatchの組み合わせが、トライ&キャッチと呼ばれるエラー処理の基本的な手法となります。

具体的なサンプルコードと共に、その動作を理解していきましょう。

enum NetworkError: Error {
    case invalidURL
    case timeout
    case unknown
}

func fetchData(from url: String) throws {
    if url == "" {
        throw NetworkError.invalidURL
    }
    // データの取得処理...
}

do {
    try fetchData(from: "")
} catch NetworkError.invalidURL {
    print("指定されたURLは無効です。")
} catch {
    print("未知のエラーが発生しました。")
}

このサンプルコードでは、前述のfetchData関数をdoブロック内でtryキーワードと共に呼び出しています。

もし関数内でエラーがスローされれば、catchブロックがそれを捕捉します。

具体的には、NetworkError.invalidURLエラーがスローされると、”指定されたURLは無効です。”というメッセージが表示されます。

それ以外のエラーは、”未知のエラーが発生しました。”というメッセージとしてキャッチされます。

○サンプルコード4:エラー情報の取得

エラーがキャッチされた際、そのエラーに関する具体的な情報を取得したい場合があります。

このような場合、catchブロック内でエラー自体を変数として扱うことができます。

do {
    try fetchData(from: "")
} catch let error as NetworkError {
    switch error {
    case .invalidURL:
        print("URLが無効です。")
    case .timeout:
        print("タイムアウトしました。")
    case .unknown:
        print("原因不明のエラーです。")
    }
}

上記のコードでは、catchブロック内でエラーをerrorという名前の変数として取得しています。

その後、switch文を使用してエラーの種類に応じた処理を行っています。

このコードを実行すると、”URLが無効です。”というメッセージが表示されることが確認できます。

●Swiftのエラー処理の応用例

Swiftのエラー処理の基本が理解できたところで、より実践的な応用例を見ていきましょう。

これにより、実際の開発場面でより効果的なエラー処理を行う力が身につきます。

○サンプルコード5:カスタムエラー情報の使用

エラーをスローする際、エラーに関する詳細情報やヒントを提供することが役立ちます。

Swiftではカスタムエラーを定義して、さまざまな情報を持たせることができます。

enum FileError: Error {
    case notFound(fileName: String)
    case insufficientPermissions(fileName: String)
}

func readFile(named: String) throws {
    if named == "secret.txt" {
        throw FileError.insufficientPermissions(fileName: named)
    }
    // ファイル読み取り処理...
}

do {
    try readFile(named: "secret.txt")
} catch FileError.notFound(let fileName) {
    print("\(fileName)というファイルは見つかりません。")
} catch FileError.insufficientPermissions(let fileName) {
    print("\(fileName)の読み取り権限がありません。")
}

上記のコードでは、FileErrorというエラー型を定義しており、notFoundinsufficientPermissionsの2つのエラーケースに、関連するfileName情報を持たせています。

このようにカスタムエラーを利用することで、エラーが発生した原因や背景を詳しく伝えることができ、デバッグやエラー対応を効率的に進めることができます。

○サンプルコード6:条件付きキャストでのエラー処理

Swiftには、オブジェクトの型変換時にas?as!というキャスト演算子が用意されています。

これを利用して、安全に型変換を行いつつエラー処理を行う方法を見ていきましょう。

class Bird {}
class Sparrow: Bird {}

let bird: Bird = Sparrow()

if let sparrow = bird as? Sparrow {
    print("スズメのインスタンスとしてキャストできました。")
} else {
    print("キャストに失敗しました。")
}

このコードでは、BirdクラスのインスタンスbirdSparrowクラスに条件付きキャストしています。

キャストが成功すれば”Sparrowのインスタンスとしてキャストできました。”というメッセージが、失敗すれば”キャストに失敗しました。”というメッセージが表示されます。

○サンプルコード7:オプショナルとの連携

Swiftのエラー処理は、オプショナルとの連携も非常に強力です。

例えば、関数がエラーをスローする可能性がある場合、その戻り値をオプショナルでラップすることで、エラー発生時にnilを返すような設計が考えられます。

func findUser(byID id: Int) throws -> String? {
    if id == 0 {
        throw UserDataError.invalidID
    }
    // ユーザー検索処理...
    return nil
}

do {
    if let user = try findUser(byID: 0) {
        print("ユーザー名: \(user)")
    } else {
        print("ユーザーは見つかりませんでした。")
    }
} catch {
    print("エラーが発生しました。")
}

このコードでは、findUser(byID:)関数がユーザーを見つけられない場合、nilを返す設計になっています。

エラーが発生した場合には、例外をスローしています。

このように、エラー処理とオプショナルの組み合わせを利用することで、より柔軟で安全なコードを実装することが可能となります。

●注意点と対処法

Swiftでエラー処理を実装する際には、いくつかの注意点とその対処法を知っておくと非常に役立ちます。

これにより、より安全で効率的なエラーハンドリングが可能となります。

○サンプルコード8:常にキャッチする例外のハンドリング

特定のエラーを指定せずにすべてのエラーをキャッチする場合、キャッチブロック内でのエラー処理が一般的すぎるという問題が発生する可能性があります。

ここでは、このような状況を表すサンプルコードを紹介します。

do {
    // エラーが発生する可能性のあるコード
} catch {
    print("何らかのエラーが発生しました。")
}

このコードでは、どんなエラーが発生しても”何らかのエラーが発生しました。”というメッセージが表示されるため、発生したエラーの詳細情報を得ることができません。

具体的なエラー情報をログや画面に表示することで、発生したエラーの原因を特定しやすくします。

do {
    // エラーが発生する可能性のあるコード
} catch let error {
    print("エラーが発生しました: \(error.localizedDescription)")
}

○サンプルコード9:特定のエラーのみキャッチする

ある特定のエラーのみを対象にエラーハンドリングを行いたい場合もあるでしょう。

このような場合、具体的なエラータイプを指定してキャッチすることが可能です。

enum NetworkError: Error {
    case timeout
    case invalidURL
    case noConnection
}

do {
    // ネットワーク関連のエラーが発生する可能性のあるコード
} catch NetworkError.timeout {
    print("接続がタイムアウトしました。")
} catch {
    print("その他のエラーが発生しました。")
}

このコードでは、NetworkError.timeoutエラーが発生した場合のみ特定のメッセージが表示されます。

それ以外のエラーは”その他のエラーが発生しました。”というメッセージが表示されるようになっています。

●カスタマイズ方法

Swiftのエラー処理は非常に柔軟であり、開発者の要求に合わせてカスタマイズすることが可能です。

ここでは、エラー処理のカスタマイズ方法とそれを実現するためのサンプルコードを紹介します。

○サンプルコード10:エラー処理のカスタム

Swiftでは、自分自身のエラー型を定義して、それを利用することができます。

例えば、特定のエラーコードやメッセージを持つカスタムエラーを作成することができます。

enum CustomError: Error {
    case specificError(code: Int, message: String)
}

func throwErrorFunction() throws {
    throw CustomError.specificError(code: 404, message: "ページが見つかりません")
}

do {
    try throwErrorFunction()
} catch CustomError.specificError(let code, let message) {
    print("エラーコード: \(code), メッセージ: \(message)")
}

上記のコードを実行すると、”エラーコード: 404, メッセージ: ページが見つかりません”と表示されます。

○サンプルコード11:エラーメッセージのカスタマイズ

エラー型に関連する情報をカスタマイズして、より具体的なエラーメッセージを生成することも可能です。

enum DetailedError: Error {
    case invalidValue(value: Int)

    var description: String {
        switch self {
        case .invalidValue(let value):
            return "無効な値: \(value) が入力されました。"
        }
    }
}

func checkValue(value: Int) throws {
    if value < 0 {
        throw DetailedError.invalidValue(value: value)
    }
}

do {
    try checkValue(value: -5)
} catch let error as DetailedError {
    print(error.description)
}

上記のコードを実行すると、”無効な値: -5 が入力されました。”と表示されます。

○サンプルコード12:再スローを使用したエラーハンドリング

Swiftでは、関数やメソッド内でエラーを捕捉し、それを再度スローすることができます。

これにより、関数やメソッドの内部でエラーハンドリングを行いつつ、外部にもエラーを伝播させることが可能となります。

func rethrowErrorFunction(value: Int) throws {
    do {
        try checkValue(value: value)
    } catch {
        print("関数内でのエラーハンドリング")
        throw error
    }
}

do {
    try rethrowErrorFunction(value: -10)
} catch let error as DetailedError {
    print("外部でのエラーハンドリング: \(error.description)")
}

このコードを実行すると、まず”関数内でのエラーハンドリング”が表示され、その後に”外部でのエラーハンドリング: 無効な値: -10 が入力されました。”と表示されます。

まとめ

Swiftのエラー処理は、開発者の要求やアプリケーションの要件に柔軟に対応できるよう設計されています。

この記事で紹介した様々な方法やテクニックを駆使することで、エラーの発生を正確にキャッチし、適切な対応を行うことができます。

初心者の方も、基本的なトライ&キャッチの仕組みから、カスタムエラーや再スローのような応用的なテクニックまで、段階的に学んでいくことで、Swiftのエラー処理の深みと可能性を探ることができるでしょう。

エラーハンドリングは、アプリケーションの品質やユーザー体験を大きく左右する要素の一つです。

Swiftの豊富なエラー処理機能を活用して、予期せぬエラーにもしっかりと対応できる堅牢なアプリケーションを開発していきましょう。

日々の開発の中で疑問や困難に遭遇した際は、この記事を再度参照することで、疑問の解消や問題の解決のヒントを得ることができるかと思います。

これからエラー処理を学び続け、より良いアプリケーション開発を目指しましょう。