目次
- やりたいこと
- 前提確認
- もしif文だけで書いていくと
- guardによる書き方
- 条件判定の処理を別クラスに切り分ける
- 最後に
やりたいこと
TextFieldに入力された値がある条件をクリアした時のみに取り込んで、それ以外の時はエラーとして認識したい時があると思います。
その時の処理の書き方を、if
文の羅列ではなく、まずはguard
を用いて書てみます。
最終的にはdo構文
とenum
を使って別クラスに処理を切り分けていきます。
前提確認
MacOS Catalina 10.15.4
Xcode 12.1
Swift version 5
ここでは仮に、あるゲームの参加者の名前をTextFieldに記入し、UIButtonを押して追加することを想定します。
但し、名前が以下の条件をクリアした時のみ、参加者として追加できることとします。
①空文字ではない
②5文字以下
もちろんもっと条件を足すこともできますが、あとは同じ作業になるので2つにしておきます。
とりあえず動けば良いと思っていた数ヶ月前の自分へ書いていますので、細かく段階を刻んでいます。
お急ぎの方は最後のコードをご覧ください。
もしif文だけで書いていくと
とりあえず動くものを作りたい、ということでif文でゴリゴリ書くとこうなるんではないでしょうか。
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文の羅列は次のように書き換えられます。
コメントアウト部分を見て頂けたら、ありがた味が分かるかと思います。
@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もご参考ください。
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
が発生したら切り出して別の処理を返す、という構文です。
書き方は次の通りですが、注意して欲しいのはdo
、try
、catch
を書かないといけないことです。
(筆者のイメージとして、「この中でエラーが発生しうる処理を実行するよ(do
)」、「さぁやってみて!(try
)」、「ほら、エラーが出てきたら逃さないよ〜(catch
)」という感じです)
do{
try ある処理 //tryを書いて、エラーが発生し得る処理であることを明示します
}catch エラーパターン1{
}catch エラーパターン2{
}catch{ //エラーパターンに漏れがないように最後はcatchだけ書く必要があります
}
④コードを書き換える
さて、準備は整いました。
①〜③をもとに処理を書き換えてみると、次のようになります。
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文の羅列をしていた筆者ですが、そんな人にも分かりやすく解説することをイメージしており、適宜細部を端折っております。もし間違いや勘違いさせるようなことが等ありましたらご指摘頂けますと幸いです。
今後もより分かりやすい・保守しやすいコードを追求していきます。