TextFieldに期待していない値が入力された時の処理の書き方-gurad,throw,do構文,enum-

#目次

* やりたいこと
* 前提確認
* もしif文だけで書いていくと
* guardによる書き方
* 条件判定の処理を別クラスに切り分ける
* 最後に

#やりたいこと

TextFieldに入力された値がある条件をクリアした時のみに取り込んで、それ以外の時はエラーとして認識したい時があると思います。
その時の処理の書き方を、`if`文の羅列ではなく、まずは`guard`を用いて書てみます。
最終的には`do構文`と`enum`を使って別クラスに処理を切り分けていきます。


#前提確認
MacOS Catalina 10.15.4
Xcode 12.1
Swift version 5

ここでは仮に、あるゲームの参加者の名前をTextFieldに記入し、UIButtonを押して追加することを想定します。
但し、名前が以下の条件をクリアした時のみ、参加者として追加できることとします。

①空文字ではない
②5文字以下

もちろんもっと条件を足すこともできますが、あとは同じ作業になるので2つにしておきます。

とりあえず動けば良いと思っていた数ヶ月前の自分へ書いていますので、細かく段階を刻んでいます。
お急ぎの方は最後のコードをご覧ください。

#もしif文だけで書いていくと
とりあえず動くものを作りたい、ということでif文でゴリゴリ書くとこうなるんではないでしょうか。

```swift:ViewController
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak private var textField: UITextField!

    //参加者を管理
    var memberArray = [String]()

    @IBAction func add(_ sender: Any) {

         if let text = textField.text{ //textField.textがnilではない場合、その中身をtextとして受け取る
             if text != ""{ //textの中身が空ではない場合のみ次の処理を行う
                if text.count <= 5{ //textが5文字以下の場合のみ次の処理を行う
                    memberArray.append(text) //Arrayに追加
                }
             }
         }
    }

}

```
ただこれだと、どこからどこまでの処理が、どのif文の中に書かれているか分かりづらいですよね。
今は条件が2つなのでまだ読めますが、更に条件を増やしていくとパッと見では分からなくなります。
(ちなみに、このように処理が多段階の入れ子式になっている状態を「ネストが深い」と言います。)

もうちょっと分かりやすく書けたらなー、、、と言うことでで登場するのが`guard`です。

#guardによる書き方

`guard`は簡潔に言うと、ある条件をクリアした時のみ処理を継続し、それ以外の時はそこで処理を止めることができます。
(筆者はいつも書く際、「この場合の値だけは守って(guard)！他はスルーでよろしく！」とイメージしています。)

書き方はとしては次のようになります。

```swift:書き方
guard クリアして欲しい条件 else{
   //条件をクリアしなかった場合 
   return //何かしらの処理を書く必要があります 
}
```

しかもこの`guard`、何が便利かと言うと、値をある定数に入れて条件判定した後、その定数は条件をクリアした値として扱うことができます。当たり前のように聞こえるかもしれませんが、処理が分かりやすくなる上でこれが結構ありがたいんです。

この`guard`を使った場合、上で書いたif文の羅列は次のように書き換えられます。
コメントアウト部分を見て頂けたら、ありがた味が分かるかと思います。

```swift:ViewController

@IBOutlet weak private var textField: UITextField!

//参加者を管理
var memberArray = [String]()

@IBAction func add(_ sender: Any) {

   guard let member = textField.text else {
      print("nilです")
      return
   }
   //これ以降memberはnilではないことが保証される

   guard member != "" else {
      print("空です")
      return
   }
   //これ以降memberは空文字ではないことが保証される

   guard member.count <= 5 else {
      print("5文字を超えています")
      return
   }
   //これ以降memberは文字数が5文字以下であることが保証される

   //上記の条件をクリアしたmemberのみが追加される
   memberArray.append(member)

}


```

いかがでしょうか。
どこでどういう条件判定をしているかだいぶ分かりやすくなりましたね。

これでも良いのですが、実際ViewControllerにはこうした処理以外にも沢山の処理が書かれています。
あとあと保守がしやすいように、この条件判定の処理だけ他に切り出してみましょう。

#条件判定の処理を別クラスに切り分ける
別クラスに条件判定の処理を書くに当たって、
①enumでの`Error`規定
②throw表記による`Error`の捕捉
③`do構文`による処理の書き方
について触れていきます。順を追って見ていきましょう。

##①enumでのError規定
ある条件をクリアしなかった場合を`enum`を使って`Error`として規定します。
`enum`の使い方はまた別の記事に譲るとして、注意して欲しいのは以下のように、`Error`protocolを適用させることです。
これを適用させることで、各`case`を`Error`として認識することができます。詳しくは[Apple Developer Document](https://developer.apple.com/documentation/swift/error)もご参考ください。

```swift:書き方
enum 名前 : Error {
        case エラーパターン1
        case エラーパターン2
    }
```

##②throw表記によるError捕捉
条件判定を行い、クリアしなかったものは`Error`として捕捉する処理を書きます。
書き方は非常にシンプルで、メソッドの`()`の後ろに`throws`(sが必要)と書き、このメソッドが`Error`が発生し得ることを明示します。
その後の処理の中で上述のguardを使用し、条件をクリアしなかった場合は`throw`(sは不要)と書いてどのエラーパターンに該当するか記載します。
(筆者のイメージは、「エラーが出てきたら投げまっせ(throw)」という感じです、そのまんまですが)

```swift:書き方
func メソッド名() throws{

        guard  クリアして欲しい条件 else {
            throw エラーパターン1
        }

         guard  クリアして欲しい条件 else {
            throw エラーパターン2
        }

    }
```



##③do構文による処理の書き方
最後に、実際に条件判定を行う際の処理を書きます。そのために`do構文`を使います。
`do構文`は簡潔に言うと、ある処理を実行し、`Error`が発生したら切り出して別の処理を返す、という構文です。
書き方は次の通りですが、注意して欲しいのは`do`、`try`、`catch`を書かないといけないことです。
(筆者のイメージとして、「この中でエラーが発生しうる処理を実行するよ(`do`)」、「さぁやってみて！(`try`)」、「ほら、エラーが出てきたら逃さないよ〜(`catch`)」という感じです)

```swift:書き方
do{
 try ある処理 //tryを書いて、エラーが発生し得る処理であることを明示します
}catch エラーパターン1{

}catch エラーパターン2{

}catch{ //エラーパターンに漏れがないように最後はcatchだけ書く必要があります

}
```

##④コードを書き換える
さて、準備は整いました。
①〜③をもとに処理を書き換えてみると、次のようになります。

```swift:ViewController
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak private var textField: UITextField!
    private var entry = Entry()

    //参加者を管理
    var memberArray = [String]()
    
    @IBAction func add(_ sender: Any) {

        do {
            let member = try entry.add(input: textField.text ?? "")
            memberArray.append(member)
        } catch Entry.Validation.isEmpty{
            print("空です")
        }catch Entry.Validation.isTooLong{
            print("5文字を超えています")
        }catch{
        }

    }

class Entry{

    enum Validation : Error {
        case isEmpty //空文字のエラーパターン
        case isTooLong //5文字を超えた時のエラーパターン
    }

    func add(input:String) throws -> String{

        guard input != "" else {
            throw Validation.isEmpty
        }

        guard input.count <= 5 else {
            throw Validation.isTooLong
        }
        return input
    }
}
```
無事に条件判定の処理を切り出せました。
これで条件の追加や、どういう処理を行いたいかを比較的簡単に管理できるかと思います。
`enum`に`associated value`としてエラーメッセージを付与させたりすると更に便利ですが、それはまた追って書くことにします。

#終わりに
もちろん他にも書き方はあり、また`gurad`や`do構文`については既に多くの解説がありますが、改めて自分なりにまとめてみました。

ほんの数ヶ月前までif文の羅列をしていた筆者ですが、そんな人にも分かりやすく解説することをイメージしており、適宜細部を端折っております。もし間違いや勘違いさせるようなことが等ありましたらご指摘頂けますと幸いです。

今後もより分かりやすい・保守しやすいコードを追求していきます。