Help us understand the problem. What is going on with this article?

[RxSwift]ユーザー登録フォーム制御実装

はじめに

https://qiita.com/doi_daihei/items/6e0330debab12bcde155
こちらの第二弾ですね。
今回もアプリ開発においてよくあるユーザー登録画面をRxで書いてみました。
ユーザー登録画面のフォーム制御ってUITextFieldのdelegateで書くと、むずかしいというより、面倒くさいですよね。
しかも、コンポーネント化しているとどこでどの制御しているか複雑になっちゃいますよね。
でもRxを用いれば、簡単かつ明瞭にかけますのでそちらをご紹介致します!

成果物

デザインはクソですが、
エラーメッセージの見せ方やsubmitボタンの活性/非活性の制御に関してはwebとかでもよく見受ける挙動ですね。

user_register.gif

ソースコード

本来であれば、MVVMだとViewModelのinputに伝搬して...とかしないといけなかったり、通信したりなど色々抜けてはいますが
今回はあくまでフォームの制御ですので、そこに着目して書いていきます。

setup

setupに関しては、enumを用意し、
コンポーネント側に用意しておいた type にbindしているだけです。

    private func setup() {
        Observable.just(.username).bind(to: usernameFormView.rx.type).disposed(by: disposeBag)
        Observable.just(.email).bind(to: emailFormView.rx.type).disposed(by: disposeBag)
        Observable.just(.password).bind(to: passwordFormView.rx.type).disposed(by: disposeBag)
        Observable.just(.confirmPassword).bind(to: confirmPasswordFormView.rx.type).disposed(by: disposeBag)
    }

制御

Rxで制御しているのは主に以下です。

  • 個々のバリデーション結果をエラーメッセージのhiddenにbind
  • 全てのバリデーション結果をsubmitボタンのenableにbind

ソースで書くと以下のような感じですね。

    private func bind() {

        let username = usernameFormView.rx.textFieldText.orEmpty.share(replay: 1)                   // share reply
        let email = emailFormView.rx.textFieldText.orEmpty.share(replay: 1)                         // share reply
        let password = passwordFormView.rx.textFieldText.orEmpty.share(replay: 1)                   // share reply
        let confirmPassword = confirmPasswordFormView.rx.textFieldText.orEmpty.share(replay: 1)     // share reply

        // ユーザー名のエラーメッセージ
        username
            .filter { !$0.isEmpty }
            .map { $0.isValidUsername() }
            .bind(to: usernameFormView.rx.isCautionLabelHidden)
            .disposed(by: disposeBag)

        // メールアドレスのエラーメッセージ
        email
            .filter { !$0.isEmpty }
            .map { $0.isValidEmail() }
            .bind(to: emailFormView.rx.isCautionLabelHidden)
            .disposed(by: disposeBag)

        // パスワードのエラーメッセージ
        password
            .filter { !$0.isEmpty }
            .map { $0.isValidPassword() }
            .bind(to: passwordFormView.rx.isCautionLabelHidden)
            .disposed(by: disposeBag)

        // 確認パスワードのエラーメッセージ
        Observable
            .combineLatest(password, confirmPassword) { password, confirmPassword in
                password == confirmPassword
            }
            .bind(to: confirmPasswordFormView.rx.isCautionLabelHidden)
            .disposed(by: disposeBag)

        // 全てのバリデーション判定をcombineLatestで最新精査
        Observable
            .combineLatest(username, email, password, confirmPassword) { username, email, password, confirmPassword in
                 username.isValidUsername() && email.isValidEmail() && password.isValidPassword() && (password == confirmPassword)
            }
            .bind(to: submitButton.rx.isEnabled)
            .disposed(by: disposeBag)
    }

コンポーネント->VCで制御するプロパティ

余談ではありますが、usernameFormView.rx.textFieldTextemailFormView.rx.textFieldText のようなコンポーネント側のRx系変数をVC側で用いたい場合が多々発生します。
その場合は以下のように書いておけば、内部で書いてあるRxの変数のように扱えます。
元々あるRxの変数のように扱えますので、パッと見でわかりますよね。

// MARK: - Reactive Extension
extension Reactive where Base: FormView {

    var type: Binder<FormType> {
        return Binder(base) { view, type in
            view.titleLabel.text = type.title
            view.cautionLabel.text = type.caution
            view.textField.keyboardType = type.keyboardType
            view.textField.isSecureTextEntry = type.isSecureTextEntry
            view.textField.textContentType = type.textContentType
        }
    }

    var textFieldText: ControlProperty<String?> {
        return base.textField.rx.text
    }

    var isCautionLabelHidden: Binder<Bool> {
        return base.cautionLabel.rx.isHidden
    }
}

まとめ

本業の方のプロジェクトがそろそろ終わりそう(絶賛炎上中ですがw)なので、
次のプロジェクトではRxを布教して行けたらなと思います。
以下、ソースコードです。

https://github.com/doihei/SignUpForm

doi_daihei
現在、株式会社andfactoryに所属しております。 前職ではフリーランス。 基本的には常駐しておりまして、並行して以下の会社のエンジニアとして所属しておりまして、複数のプロジェクトに参画。 https://ks-rogers.co.jp/ バックエンドからフロントエンド、チームリーダー、プログラミング講師などエンジニアとして幅広く経験をする。
https://andfactory.co.jp/
andfactory
Smartphone Idea Companyとして、人々の生活に「&(アンド)」を届ける。
https://andfactory.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした