エラーがスローされると、周囲のコードがそのエラーを処理する、例えば問題を修正、別の手段を試行、ユーザに失敗を通知などの責任を負います。

Swift にはエラーを処理する方法が 4 つあります。関数からその関数を呼び出したコードにエラーを伝播させる、docatch 文を使ってエラーを処理する、オプショナル値としてエラーを処理する、あるいはエラーが発生しないとアサートすることができます。各アプローチについて以降で説明しています。

関数がエラーをスローするとき、プログラムのフローが変わるため、エラーをスローするコードの場所をすばやく特定できることが重要です。コードの場所を特定するために、エラーをスローする可能性がある関数やメソッド、イニシャライザを呼び出すコードの前に、try キーワード、あるいは try? や try! を記述します。これらのキーワードについて以降で説明しています。

NOTE
Swift のエラー処理は、trycatchthrow キーワードを使用する他の言語での例外処理に似ています。Objective-C を含む多くの言語での例外処理と異なり、Swift のエラー処理は、処理コストが高いコールスタックを巻き戻す処理を伴いません。例として、throw 文の性能特性は、return 文と同等です。

スローする関数でエラーを伝播

関数やメソッド、イニシャライザがエラーを投げる可能性があることを示すために、関数の宣言でパラメータの後に throws キーワードを記述します。throws が付けられた関数をスローする関数と呼びます。関数が戻り値の型を指定している場合には、throws キーワードをリターンアロー (->) の前に記述します。

func canThrowErrors() throws -> String

func cannotThrowErrors() -> String

スローする関数は、関数内でスローされたエラーを、関数が呼び出されたスコープに伝播します。

NOTE
スローする関数のみエラーを伝播させることができます。スローしない関数でスローされたエラーは、関数内で処理する必要があります。

次の例は、リクエストされた商品が利用できない場合や、売り切れ、金額不足の場合に、適切な VendingMachineError をスローする vend(itemNamed:) メソッドを持つ VendingMachine クラスです。

struct Item {
    var price: Int
    var count: Int
}

class VendingMachine {
    var inventory = [
        "Candy Bar": Item(price: 12, count: 7),
        "Chips": Item(price: 10, count: 4),
        "Pretzels": Item(price: 7, count: 11)
    ]
    var coinsDeposited = 0
    func dispenseSnack(snack: String) {
        print("Dispensing \(snack)")
    }

    func vend(itemNamed name: String) throws {
        guard let item = inventory[name] else {
            throw VendingMachineError.InvalidSelection
        }

        guard item.count > 0 else {
            throw VendingMachineError.OutOfStock
        }

        guard item.price <= coinsDeposited else {
            throw VendingMachineError.InsufficientFunds(coinsNeeded: item.price - coinsDeposited)
        }

        coinsDeposited -= item.price

        var newItem = item
        newItem.count -= 1
        inventory[name] = newItem

        dispenseSnack(name)
    }
}

vend(itemNamed:) メソッドの実装は、スナックを購入するための要件を満たしていない場合に、メソッドを早期に終了して適切なエラーをスローするために guard 文を使用しています。throw 文はプログラムの制御を即座に転送するため、すべての要件を満たしている場合にのみ商品は販売されることになります。

vend(itemNamed:) メソッドはスローするエラーを伝播するため、このメソッドを呼び出すコードは docatch 文、あるいは try? や try! を使用してエラーを処理するか、エラーを伝播し続ける必要があります。例えば、次の例での buyFavoriteSnack(_:vendingMachine:) もスローする関数で、vend(itemNamed:) メソッドがスローするエラーは関数 buyFavoriteSnack(_:vendingMachine:) が呼び出されたところまで伝播されます。

let favoriteSnacks = [
    "Alice": "Chips",
    "Bob": "Licorice",
    "Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
    let snackName = favoriteSnacks[person] ?? "Candy Bar"
    try vendingMachine.vend(itemNamed: snackName)
}

この例では、関数 buyFavoriteSnack(_:vendingMachine:) は与えられた人のお気に入りのスナックを調べ、vend(itemNamed:) メソッドを呼び出して購入しようとします。vend(itemNamed:) メソッドはエラーをスローする可能性があるため、その前に try キーワードを付けて呼び出されています。

スローするイニシャライザは、スローする関数と同じようにエラーを伝播することができます。例えば、次のコードでの構造体 PurchasedSnack のイニシャライザは、初期化処理の一部としてスローする関数を呼び出し、イニシャライザの呼び出し元に伝播してエラーを処理します。

struct PurchasedSnack {
    let name: String
    init(name: String, vendingMachine: VendingMachine) throws {
        try vendingMachine.vend(itemNamed: name)
        self.name = name
    }
}

do-catch でエラーを処理

コードブロックを実行してエラーを処理するために docatch 文を使用します。do 節のコードでエラーがスローされた場合、どの catch 節がエラーを処理できるか決定するために照合されます。

次は docatch 文の一般的な形式です。

do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
}

catch 節が処理できるエラーを示すために catch の後にパターンを記述します。catch 節にパターンが無い場合には、その節はどのエラーにもマッチし、ローカル定数名の error にエラーをバインドします。パターンマッチについての詳細な情報は、Patterns を確認してください。

do 節のコードがスローする可能性があるすべてのエラーを、catch 節で処理する必要はありません。どの catch 節もエラーを処理しない場合、エラーは周囲のスコープに伝播します。しかしながら、エラーを処理する docatch 節に入れるか、スローする関数内にするかして、周囲のスコープでエラーを処理する必要があります。例えば、次のコードでは列挙型 VendingMachineError の 3 つのケースすべてを処理していますが、他のすべてのエラーは周囲のスコープで処理する必要があります。

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.InvalidSelection {
    print("Invalid Selection.")
} catch VendingMachineError.OutOfStock {
    print("Out of Stock.")
} catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
}
// "Insufficient funds. Please insert an additional 2 coins." と出力

この例では、関数 buyFavoriteSnack(_:vendingMachine:) がエラーをスローする可能性があるため、try 式で呼び出されています。エラーがスローされた場合、実行は即座に伝播し続けるかを決定する catch 節に転送されます。エラーがスローされない場合は、do 文の残りが実行されます。

エラーをオプショナル値に変換

エラーをオプショナル値に変換してエラーを処理するために try? を使用します。try? 式を評価している間にエラーがスローされた場合、式の値は nil になります。例として、次のコードの x と y は、同じ値で同じ振る舞いになります。

func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()

let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}

someThrowingFunction() がエラーをスローする場合、x と y の値は nil になります。そうでない場合には、x と y の値は関数が返した値になります。x と y は、someThrowingFunction() が返す型のオプショナルであることに注目してください。関数が整数を返す場合には、x と y はオプショナルの整数となります。

try? を使用することで、すべてのエラーを同じように処理したい場合に、簡潔なエラー処理コードを記述することができます。例として、次のコードはデータを取り出すためにいくつかの手段を使用し、すべての手段が失敗する場合には nil を返します。

func fetchData() -> Data? {
    if let data = try? fetchDataFromDisk() { return data }
    if let data = try? fetchDataFromServer() { return data }
    return nil
}

エラー伝播を停止

スローする関数やメソッドが、実際には実行時にエラーをスローしないことがわかっている場合があります。そうしたときには、エラー伝播を停止し、エラーがスローされないという実行時のアサーションで呼び出しをラップするために、式の前に try! を記述することができます。エラーが実際にスローされた場合には、実行時エラーになります。

例として、次のコードは与えられたパスの画像リソースをロードし、画像をロードできない場合にはエラーを投げる関数 loadImage(_:) を使用しています。このケースでは、画像がアプリケーションに搭載されているため、実行時にエラーがスローされることはなく、エラー伝播の停止に適しています。

let photo = try! loadImage("./Resources/John Appleseed.jpg")

Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.