LoginSignup
5

More than 1 year has passed since last update.

posted at

updated at

【SwiftUI】Combineを使った入力フォームのバリデーションチェック

Combineを使ってTextFieldの入力のバリデーションチェック

入力フォームのバリデーションチェックを、Combineで実行します。

開発環境

  • macOS 11
  • Xcode 12 beta

View側の実装

全体のソースコード。


struct CreateUserView: View {

    @StateObject var vm = CreateUserViewModel()

    var body: some View {
        NavigationView {
            Form {
                TextField("Username", text: $vm.username)
                TextField("Password", text: $vm.password)
                TextField("Password Again", text: $vm.passwordAgain)
                Button(action: {}, label: {
                    Text("Submit")
                })
                .disabled(!vm.readyToCreate)

                Section(header: Text("DEBUG")) {
                    Text(vm.readyToCreate ? "Ready to create" : "Waiting input")
                }
            }
            .navigationTitle("Create User")
        }
    }
}

Formでリストを構築し、ユーザー名のTextFieldと、パスワードと二回目パスワードのSecureFieldを入れる。

入力された情報は、ViewModelに持たせて、そこでバリデーションチェックを行う。
readyToCreateは、一回目パスワードと二回目パスワードが一致する、かつユーザー名の空ではないときにtrueになる。

Submitボタンは、バリデーションチェック結果のreadyToCreateTrueの場合のみ活性化になる。

デバッグのため、下にreadyToCreateの値を表示する。

※Xcode 12 RC/iOS14.2 でSecureFieldがバグっていて上手く表示されないこともある。その時はTextFieldに変えてください

ViewModel側の実装

全体のソースコード。


class CreateUserViewModel: ObservableObject {
    @Published var username = ""
    @Published var password = ""
    @Published var passwordAgain = ""
    @Published var readyToCreate = false

    private var anyCancellable = Set<AnyCancellable>()

    init() {
        $username
            .combineLatest($password, $passwordAgain)
            .map {
                let username = $0.0
                let password = $0.1
                let passwordAgain = $0.2

                guard username.count > 0, password.count > 0, passwordAgain.count > 0 else { return false }
                guard password == passwordAgain else { return false }
                return true
            }
            .assign(to: \.readyToCreate, on: self)
            .store(in: &anyCancellable)
    }
}

①ストリームを合成する


$username
    .combineLatest($password, $passwordAgain)

combineLatestを使って$username$password$passwordAgainの3つのストリームを結合させる。
どちらかが新しい値が出力されたとき、3ストリームの一番新しい値がタプルにとして再出力される。
combineLatestの詳しい説明は、こちらを参考ください。

②バリデーションチェック


.map {
    let username = $0.0
    let password = $0.1
    let passwordAgain = $0.2

    guard username.count > 0, password.count > 0, passwordAgain.count > 0 else { return false }
    guard password == passwordAgain else { return false }
    return true
}

mapを使ってusernamepasswordpasswordAgainの文字数をチェックする。
$password$passwordAgainが一致するのをチェックする。
両方チェックOKのみtrueを出力する。それ以外はfalseを出力する。

③画面更新


.assign(to: \.readyToCreate, on: self)

mapで出力されたパスワードチェックの結果をreadyToCreateに設置する。
readyToCreateが更新されると、View側も更新される。

④AnyCancellable


.store(in: &anyCancellable)

AnyCancellableをストアする。

まとめ

SwiftUIとCombineを使えば、簡単にバリデーションチェックを作れます。
両方ともiOS13以降しか使えないのがネックですが、今後は大きく期待できると思います。
(みんなFlutterで盛り上がっているみたいですが。。。)

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
What you can do with signing up
5