💭
SwiftUIで登録フォームを作る
今回はSwiftUIで登録フォームを作成します。
一般的な登録・ログインフォームでは、メールアドレスの形式になっているかとか、2つのパスワードが一致しているかとかのバリデーションを行います。SwiftUIでどのように実現するのかを解説していきます。
必要なフレームワーク
特にCombine
フレームワークでは、以下のクラスをメインに使います。
Published
-
ObservableObject
,ObservedObject
実装
ViewModelの作成
Viewに関連するロジックをViewModel
として実装します。SwiftUIに値を反映させたいので、ObservableObject
を継承しています。
import Foundation
import Combine
class ViewModel: ObservableObject {
@Published var mail = "" //メールアドレスの入力値を格納
@Published var pass = "" //パスワードの入力値を格納
@Published var retype = "" //パスワードの再入力値を格納
@Published var canSend = false //登録できるかどうかのフラグ
@Published var invalidMail = "" //無効なメールアドレスの時のエラー文言
@Published var invalidPass = "" //無効なパスワードの時のエラー文言
init() {
//①登録できるかどうかの判定
let mailValidation = $mail.map({ !$0.isEmpty && $0.isValidEmail }).eraseToAnyPublisher()
let passValidation = $pass.map({ !$0.isEmpty }).eraseToAnyPublisher()
let retypeValidation = $retype.map({ !$0.isEmpty }).eraseToAnyPublisher()
let matchValidation = $pass.combineLatest($retype).map({ $0 == $1 }).eraseToAnyPublisher()
Publishers.CombineLatest4(mailValidation, passValidation, retypeValidation, matchValidation)
.map({ [$0.0, $0.1, $0.2, $0.3] }) //4つの条件(bool)を1つの配列にまとめます
.map({ $0.allSatisfy{ $0 } }) //配列になった4つの条件(bool)が全てtrueの時に、結果がtrueとなります
.assign(to: &$canSend) //結果をcanSendに格納します。
//②メールアドレスのチェック
$mail.map({ $0.isEmpty || $0.isValidEmail ? "" : "enter valid mail address" }).assign(to: &$invalidMail)
//③パスワードのチェック
$pass.combineLatest($retype)
.filter({ !$0.1.isEmpty && !$0.1.isEmpty })
.map({ $0.0 == $0.1 ? "" : "must match password" })
.assign(to: &$invalidPass)
}
}
extension String {
//メールアドレスの形式になっているかどうかの判定
var isValidEmail: Bool {
let emailRegEx = "[A-Z0-9a-z._+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
return emailTest.evaluate(with: self)
}
}
①登録できるかどうかの判定
ここの実装がやや複雑ですが、やりたいこととしては
- メールアドレスが有効かどうか
- 2つのパスワードが入力されているかどうか
- 2つのパスワードが一致しているかどうか
を判定(bool)し、全ての条件が揃ったかどうか(bool)をcanSend
に格納しています。
map
を使い、それぞれString
からBool
に型変換をしています。CombineLatest
を使うことで、2つ以上の条件を合成しています。これらの機能を使うことにより、複雑な条件を組むことができます。
CombineLatest
についてはこちらの記事が参考になります。
②メールアドレスのチェック
登録できるかとは別に、メールアドレスの形式のチェックを行います。不正なメールアドレスの形式の場合はinvalidMail
に文言が入ります。
③パスワードのチェック
2つのパスワードが一致しているかどうかを判定し、一致していない場合はinvalidPass
に文言が入ります。
Viewの作成
Viewを作成します。先程のViewModel
をObservedObject
として持っています。
Viewのスタイルについては一部省略しています。
struct ContentView: View {
//ViewModel
@ObservedObject var viewModel: ViewModel = .init()
var body: some View {
VStack {
Group {
//メールアドレスを格納
TextField.init("mail address", text: self.$viewModel.mail) .textContentType(.emailAddress)
if !self.viewModel.invalidMail.isEmpty { //メールアドレスが不正な場合
Text(self.viewModel.invalidMail)
.foregroundColor(.red)
}
}
Group {
//パスワードを格納
SecureField.init("password", text: self.$viewModel.pass)
.textContentType(.newPassword)
//もう一つのパスワードを格納
SecureField.init("retype password", text: self.$viewModel.retype)
.textContentType(.newPassword)
if !self.viewModel.invalidPass.isEmpty { //パスワードが一致しない場合
Text(self.viewModel.invalidPass)
.foregroundColor(.red)
}
}
Button("Register") {
debugPrint("register")
}.disabled(!self.viewModel.canSend) //登録できる場合はボタンが有効化される
.foregroundColor(.blue)
Spacer()
}
}
}
TextField
とSecureField
にそれぞれ値を入力すると、入力する度にmail
, pass
, retype
の3つから登録できるかどうかcanSend
を判定します。
canSend
が変更されれば、Button
の状態が変更されます。
スクリーンショット
サンプルコード
サンプルコードはこちらに置いてあります。
Discussion