どんなエラーが起こったかユーザーに伝えたいということで、、、

## 流れ
1. どうして勉強したいと思ったのか？
1. キーワード
1. エラー処理するための準備
1. 実際にメソッドを実行してみる
1. 伝播と捕捉

## どうして勉強したいと思ったのか？
アプリを作っている過程で、処理が想定通りに進まないなということが出てきた場合に、ユーザーにエラーの内容を伝えたいと思いエラー処理について勉強しました。今回は`enum`を`Error`型(今回は`LocalizedError`)に適合させてその`case`でユーザーにエラーの内容を伝える方法を記述していきたいと思います。

#### 今回はこんな感じでアラートを表示させます。
![スクリーンショット 2023-11-03 21.12.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3232025/4626ac3b-8f72-40e6-af03-b43412d31ddd.png)


## キーワード
『do』
『catch』
『throw ・ throws』
『try』
『LocalizedError』
『alert(isPresented:error:actions:message:)』 SwiftUIのインスタンスメソッド

https://developer.apple.com/documentation/swiftui/view/alert(ispresented:error:actions:message:)

## エラー処理するための準備
`enum`に`LocalizedError`を適合させます。`LocalizedError`に適合させる理由としては,今回使用するアラート`alert(isPresented:error:actions:message:)`インスタンスメソッドのerrorの引数は、この`LocalizedError`に適合した引数を受け取るためです。
```swift
enum TextError: LocalizedError {
    case parseError
    case emptyError
    case unknown
    var errorDescription: String? {
        switch self {
        case .parseError:
            return "数字を入力してください"
        case .emptyError:
            return "入力されていません"
        case .unknown:
            return "原因がわかりません"
        }
    }
}
```
次に入力値をチェックするためのメソッドを記述します。
1. この関数はエラーを投げる可能性があるため、引数リストの直後に`throws`キーワードを記述します。
1. もしも引数numTextが空だった場合、`throw`文のTextError.emptyErrorを実行し、エラーを投げます。numParseメソッドもパースできなかった場合にエラーを投げるよう文を記述します。
1. 全てのチェックを完了したら、プロパティnum（Int型）を返します。
```swift
func parseNumberFromString(numText: String) throws -> Int {
        guard !numText.isEmpty else {
            throw TextError.emptyError
        }
        let numParse: (String) -> Int? = { str in Int(str) }
        guard let num = numParse(numText) else {
            throw TextError.parseError
        }
        return num
    }
```

## 実際にメソッドを実行してみる。

上で作ったメソッドを動かしてみます。ここで重要になってくるのは、**２つ**です。
- 『do - try - catch』
- 『.alert(isPresented: $showError, error: errorType)』

`do`スコープ内は成功した結果を返してくれます。ここで**忘れてはいけない**のが、エラーを投げる関数の前に`try`キーワードをつけるということです。
`catch`スコープ内は投げられたエラーをキャッチしてくれます。このスコープ内では、投げられたエラーの値は`error`という名前で参照が可能です。print(error)で中身がみれます。今回のサンプルコードでは最後の`catch`スコープには、errorをTextError型にキャストしていないものがあります。これは、未知のerrorが返された時のために、不明なエラーとして、errorTypeに.unknownを代入しています。
`.alert(isPresented: $showError, error: errorType)`メソッドには、error引数に対して、TextError型のerrorTypeを渡します。

```swift
struct ErrorTest: View {
    
    @State var numText = ""
    @State var errorType: TextError? = nil
    @State var num: Int? = nil
    @State var showError = false
    
    var body: some View {
        ZStack{
            Color.blue.ignoresSafeArea()
            VStack{
                TextField("", text: $numText)
                    .textFieldStyle(.roundedBorder)
                    .padding()
                Button("解析"){
                    do {
                        num =  try parseNumberFromString(numText: numText)
                    } catch let error as TextError {
                        showError = true
                        errorType = error
                    } catch {
                        showError = true
                        errorType = .unknown
                    }
                }
                .foregroundColor(.white)
                .padding()
                VStack{
                    if let num = num {
                        Text("数字は：\(num)です")
                    } else {
                        Text("数字なし")
                    }
                }
                .foregroundColor(.white)
                .padding()
            }
            .alert(isPresented: $showError, error: errorType) {
                Button("承知"){
                    //...処理を記述
                }
            }
        }
    }
}
```

## 伝播と捕捉
下のサンプルコードはエラーの`伝播`と`捕捉`を表すために書きました。まず初めに、`捕捉`とは、エラーの発生を把握して対応する手続きを起動することを指します。f1メソッドを呼ぶとf2メソッドが呼ばれ、f2メソッドを呼ぶとf3メソッドが呼ばれるという仕組みになっています。そして、f3メソッドはエラーを投げます。このときf3メソッドではエラーの捕捉（キャッチ）は起きません。最初の呼び出し元まで遡って伝播していきます。この時`throw`と`try`が書かれた文の上のコードは実行されますが、下のコードは実行されません。do - catchでエラーを捕捉できれば、対応するcatchスコープの処理が実行される仕組みになっています。

```swift

enum ErrorType: Error {
    case one
    case two
    case three
}

do {
   try f1()
} catch let error as ErrorType {
    print(error) //⭐️three
}  catch {
    print("unknown")
}

func f1() throws {
    print("実行される")
    try f2()
    print("実行されない")
}

func f2() throws {
    try f3()
}

func f3() throws {
    let num:Int? = nil
    guard let num = num else {
        print("実行される")
        throw ErrorType.three
        print("実行されない")
    }
}
```

## まとめ
- エラーを適切に処理し、ユーザーに理解しやすいメッセージを提供することで、ユーザーはどのように対処すればよいのか理解することができると思います。ユーザーの満足度を上げるためにもこれからリリースするアプリにしっかり導入していきたいと思います。突然クラッシュなんかしたら、ユーザーはびっくりしますよね。。。
- エラー処理を記述することで、開発していく過程でどこでエラーが起こっているのか理解しやすくなりました。アプリを開発していく上で必須だなと感じました。
- 勉強途中で至らぬところは是非ご指摘して頂けたらと思います。

## 参考書籍

[解読swift第５版](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-Swift-%E7%AC%AC5%E7%89%88-%E8%8D%BB%E5%8E%9F-%E5%89%9B%E5%BF%97/dp/481560407X/ref=sr_1_1?adgrpid=53077665117&hvadid=679018699393&hvdev=c&hvlocphy=1009540&hvnetw=g&hvqmt=e&hvrand=4215105661254308552&hvtargid=kwd-357414647674&hydadcr=15817_13711664&jp-ad-ap=0&keywords=%E8%A9%B3%E8%A7%A3+swift&qid=1699019664&sr=8-1)