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

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

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

![user_register.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369939/d2d40664-bad2-7e47-37bc-ec71c215477f.gif)

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

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

```swift
    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

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

```swift
    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.textFieldText` や `emailFormView.rx.textFieldText` のようなコンポーネント側のRx系変数をVC側で用いたい場合が多々発生します。
その場合は以下のように書いておけば、内部で書いてあるRxの変数のように扱えます。
元々あるRxの変数のように扱えますので、パッと見でわかりますよね。

```swift

// 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
    }
}
```

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

https://github.com/doihei/SignUpForm