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ボタンは、バリデーションチェック結果のreadyToCreate
がTrue
の場合のみ活性化になる。
デバッグのため、下に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を使ってusername
、password
とpasswordAgain
の文字数をチェックする。
$password
と$passwordAgain
が一致するのをチェックする。
両方チェックOKのみtrue
を出力する。それ以外はfalse
を出力する。
③画面更新
.assign(to: \.readyToCreate, on: self)
mapで出力されたパスワードチェックの結果をreadyToCreate
に設置する。
readyToCreate
が更新されると、View側も更新される。
④AnyCancellable
.store(in: &anyCancellable)
AnyCancellable
をストアする。
まとめ
SwiftUIとCombineを使えば、簡単にバリデーションチェックを作れます。
両方ともiOS13以降しか使えないのがネックですが、今後は大きく期待できると思います。
(みんなFlutterで盛り上がっているみたいですが。。。)