【Swift】do-catchとthrows文の使い方!エラーハンドリングのやり方

この記事からわかること

  • Swiftエラー処理(エラーハンドリング)の実装方法
  • NSErrorクラスErrorプロトコルの違い
  • do-catch文の使い方
  • throwthrows違い
  • deferステートメントとは

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

Swiftのエラーハンドリングの方法をまとめていきます。

Handling Cocoa Errors in Swift
Error Handling

エラーハンドリングとは?

そもそもエラーハンドリングとはプログラム実行中に発生したエラーを適切に対象する処理のことです。様々なプログラミング言語でも使用される事柄であり、エラー処理や例外処理とも呼ばれます。

もちろんSwiftでもエラーハンドリングが行えるようにさまざまなものが用意されています。ポイントになってくるのは以下の5つです。

エラーの定義や発火、その後の処理方法などを上記のポイントを使い分けながら処理することができます。ちなみにステートメントは文とも呼ばれたりしています。

Swift:Statements

NSErrorクラス

Swiftでのエラー処理はErrorプロトコルとして定義されており、Objective-CのNSErrorクラスと互換性が保たれています。まずはNSErrorクラスを見てみます。

公式リファレンス:NSErrorクラス

class NSError : NSObject

NSErrorクラスはObjective-Cで使用されていたエラークラスでエラーに関する3つの情報を保持しています。

var code: Int  // エラーコード
var domain: String // エラードメインを含む文字列
var userInfo: [String : Any] // ユーザー情報

Errorプロトコル

公式リファレンス:Errorプロトコル

protocol Error : Sendable

ErrorプロトコルはSwiftにおけるエラー情報管理するためのプロトコルです。定義するときはenum(列挙型)などに準拠させ発生しうるエラー内容を列挙しておきます。

enum conversionError: Error {
    case failed   // 変換失敗
    case overflow // オーバーフロー
}

おすすめ記事:【Swift】enum(列挙型)の使い方!値型enumと関連型enumとは

NSErrorクラスとは互換性があるので、キャスト(型変換)することが可能です。

var error = ConversionError.overflow
let nserror = error as NSError
print(nserror.code) // 1

throwステートメント:エラーを投げる

Swiftではthrowステートメントを使ってエラーを投げることができます。

throw ConversionError.failed

throwの後には発生させたいErrorオブジェクトを渡します。NSErrorも投げることが可能です。

throw NSError(domain: String, code: Int, userInfo: nil)

throwsキーワード:エラー発生

エラーを発生させる処理が組み込まれた関数やメソッド、イニシャライザにはエラーをスローできることを示すためにthrowsキーワードをつける必要があります。これによりエラーをスコープ外に伝播させることができるようです。

func test() throws {
  // 
}

”Only throwing functions can propagate errors. Any errors thrown inside a nonthrowing function must be handled inside the function.”
訳:スロー関数のみがエラーを伝播できます。非スロー関数内でスローされたエラーは、関数内で処理する必要があります。
引用元

例えば以下は文字列形式で受け取った数字を数値に変換するメソッドです。変換が失敗した時と10以上の値を渡された時にエラーが発生するようになっています。

func conversionNum(numStr:String) throws -> Int? {
    guard let num = Int(numStr) else {
        throw ConversionError.failed
    }
    if num > 10 {
        throw ConversionError.overflow
    }
    return num
}

do-catchステートメント

エラーの発生はdo-catchステートメントを使って捕捉します。doステートメントとcatchステートメントの2つで構成されており、do内でエラーがthrowされる可能性のあるメソッドを呼び出しcatch内でそのエラーを参照することができるようになります。

do {
   // エラーがthrowされる可能性のあるメソッドの呼び出し
} catch {
   // エラーの捕捉
   print(error)
}

catchブロックではerrorパラメータが宣言されているので補足したエラーオブジェクトがerrorに格納されます。カスタムエラーの場合はキャストすることで特定のエラータイプにアクセスすることが可能になります。

tryキーワード

tryキーワード

do内でエラーをthrowするメソッドを呼び出すにはtryキーワードをつけます。またtry?/try!を使用することでエラーを無視する(do-catchを使わない)ことも可能です。

try expression

try

エラーを捕捉したい場合はdo-catchtryを使用して以下のように記述することでエラー処理を行うことができます。

do {
    try conversionNum(numStr: "0")
} catch {
    print("エラー発生")
}

try?

try?を使用する場合はエラーが発生しても無視でき、コンパイルエラーが発生することはありません。

try? conversionNum(numStr: "0")

try!

try!を使用する場合はエラーの発生を無視できますが、エラーが実際に発生した場合ランタイムエラーが発生してしまいます。なのでtry!はエラーが確実に発生せず、エラーの伝播が必要ない時などに使用します。

try! conversionNum(numStr: "0")

全体のサンプルコード

import Foundation

enum ConversionError:Error{
    case failed
    case overflow
}

func conversionNum(numStr:String) throws -> Int? {
    guard let num = Int(numStr) else {
        throw ConversionError.failed
    }
    if num > 10 {
        throw ConversionError.overflow
    }
    return num
}

do {
    try conversionNum(numStr: "11")
} catch {
    print("エラー発生")
}

deferステートメント

エラーが発生すると一連の流れの処理が中断され、最後に必ず実行したい処理があっても呼び出されないまま終了してしまいます。これを解決するのがdeferステートメントです。

deferステートメントは通常の処理の最後、またはエラーが発生した直後に実行される処理を定義できるステートメントです。do-catch内で記述し、処理が終了またはエラーの発生したタイミングまで実行を延期します。後続で行いたい処理をキューに溜めていくイメージですかね?

do {
    defer { print("処理1") }
    try conversionNum(numStr: "11")
    defer { print("処理2") }
} catch {
    print("エラー発生")
}

例えば上記の場合エラーが発生しなければ「処理2処理1」が表示されますが、エラーが発生した場合は「処理1エラー発生」で終了します。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index