9
6

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.

iOSAdvent Calendar 2020

Day 12

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

Last updated at Posted at 2020-12-11

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で盛り上がっているみたいですが。。。)

9
6
1

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
9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?