LoginSignup
7
6

More than 3 years have passed since last update.

RxSwift + MVVMサンプルアプリ1(バリデーション付き登録フォーム)

Posted at

はじめに

MVVMの勉強のアウトプットとして簡単なサンプルを作ったので紹介します。
これ以上ないくらい簡単なサンプルになっているので、RxSwift + MVVM初学者の方向けとなります。(著者も初心者です笑)

作ったもの

サインアップ時のバリデーションを行うサンプルを作りました。
正しい入力でなければSignUpButtonを押せないようにしています。ModelはないのでViewとViewModelのみの実装となります。
スクリーンショット 2021-04-13 8.51.01.png
スクリーンショット 2021-04-13 8.51.09.png

準備

前準備として正しいメールアドレスかを判定するメソッドを用意しておきます。

class Validator {

    static func isEnableEmail(email: String) -> Bool {
        let args = "[A-Z0-9a-z._+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
        let validation = NSPredicate(format: "SELF MATCHES %@", args)
        return validation.evaluate(with: email)
    }
}

実装

まずは、ViewModelから実装していきます。
(写経するとデータの流れを把握できると思うので、手を動かして理解することをお勧めします。)

ViewModel

protocol ViewModelInputs {
    var email: BehaviorRelay<String> { get }
    var password: BehaviorRelay<String> { get }
}

protocol ViewModelOutputs {
    var isSignUpButtonEnabled: Driver<Bool> { get }
}

protocol ViewModelType {
    var inputs: ViewModelInputs { get }
    var outputs: ViewModelOutputs { get }
}

class ViewModel: ViewModelType, ViewModelInputs, ViewModelOutputs {

    var inputs: ViewModelInputs { return self }
    var outputs: ViewModelOutputs { return self }

    //MARK: - Inputs
    var email = BehaviorRelay<String>(value: "")
    var password = BehaviorRelay<String>(value: "")

    //MARK: - Outputs
    var isSignUpButtonEnabled: Driver<Bool>

    init() {

        isSignUpButtonEnabled = Observable.combineLatest(email, password)
            .map({ (email, password) -> Bool in
                return Validator.isEnableEmail(email: email) && password.count > 5
            })
            .asDriver(onErrorDriveWith: .empty())
    }
}

Inputs

emailとpasswordはViewの入力を受け取るためのプロパティです。
BehaviorRelayは初期値を設定できて、onNextのみ流れるObservableです。

Outputs

isSignUpButtonEnabledをViewからバインドしてSignUpButtonのisEnabledに反映させます。

init()

Outputs
OutputsのisSignUpButtonEnabledに入力結果を評価してBoolに変換したものを格納していきます。
まず、emailとpasswordをcombineLatestで1つのObservableに集約し、mapで(String, String) -> Boolへの変換を行います。
Observableから.asDriver(onErrorDriveWith: .empty())を用いることでDriverへ変換しています。

View

class ViewController: UIViewController {

    @IBOutlet weak var emailTextField: UITextField!
    @IBOutlet weak var passwordtextField: UITextField!
    @IBOutlet weak var signUpButton: UIButton!


    private let bag = DisposeBag()
    private var viewModel: ViewModelType!

    override func viewDidLoad() {
        super.viewDidLoad()
        viewModel = ViewModel()

        emailTextField.rx.text.orEmpty
            .bind(to: viewModel.inputs.email)
            .disposed(by: bag)

        passwordtextField.rx.text.orEmpty
            .bind(to: viewModel.inputs.password)
            .disposed(by: bag)

        viewModel.outputs.isSignUpButtonEnabled
            .drive(signUpButton.rx.isEnabled)
            .disposed(by: bag)

    }

}

emailTextFieldとpasswordTextFieldの入力値をViewModelの入力用プロパティにバインドします。
ViewModelからのOutputであるisSignUpButtonEnabledをsignUpButtonのisEnabledにバインドし、バリデーションの結果を反映させます。
Driverをバインドするときはbindではなくdriveを使用します。

これで完成です!

今後

今回はボタンのisEnabledしか操作していませんが、少し複雑にしたバージョンも作りたいと思っています。
最後まで読んでいただきありがとうございました!

7
6
0

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