1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

目次

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

やりたいこと

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

前提確認

MacOS Catalina 10.15.4
Xcode 12.1
Swift version 5

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

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

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

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

もしif文だけで書いていくと

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

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)!他はスルーでよろしく!」とイメージしています。)

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

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

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

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

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の使い方はまた別の記事に譲るとして、注意して欲しいのは以下のように、Errorprotocolを適用させることです。
これを適用させることで、各caseErrorとして認識することができます。詳しくはApple Developer Documentもご参考ください。

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

②throw表記によるError捕捉

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

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

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

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

    }

③do構文による処理の書き方

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

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

}catch エラーパターン2{

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

}

④コードを書き換える

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

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
    }
}

無事に条件判定の処理を切り出せました。
これで条件の追加や、どういう処理を行いたいかを比較的簡単に管理できるかと思います。
enumassociated valueとしてエラーメッセージを付与させたりすると更に便利ですが、それはまた追って書くことにします。

終わりに

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

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

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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?