使用复选框按钮验证表单 RxCocoa/RxSwift

validate form with checkbox button RxCocoa/RxSwift

我需要用单选按钮验证表单,但我做不到,我分享我的代码:

我的看法:

private func registerForm() {
    tvUserName.rx.text.map { [=11=] ?? "" }.bind(to: viewModel.userNamePublishSubject).disposed(by: disposeBag)
    tvEmail.rx.text.map { [=11=] ?? "" }.bind(to: viewModel.emailPublishSubject).disposed(by: disposeBag)
    tvPassword.rx.text.map { [=11=] ?? "" }.bind(to: viewModel.passwordPublishSubject).disposed(by: disposeBag)
    tvRepeatPassword.rx.text.map { [=11=] ?? "" }.bind(to: viewModel.repeatPasswordPublishSubject).disposed(by: disposeBag)
    viewModel.formLoginNativaIsValid().bind(to: btnNext.rx.isEnabled).disposed(by: disposeBag)
    
    checkBoxOutlet.rx.tap
    .scan(false) { lastValue, _ in
        return !lastValue
    }
    .bind(to: btnNext.rx.isEnabled)
    .disposed(by: disposeBag)
}

我的视图模型:

let userNamePublishSubject = PublishSubject<String>()
let emailPublishSubject = PublishSubject<String>()
let passwordPublishSubject = PublishSubject<String>()
let repeatPasswordPublishSubject = PublishSubject<String>()

func formLoginNativaIsValid() -> Observable<Bool> {
    return Observable.combineLatest(userNamePublishSubject.asObserver(),
        emailPublishSubject.asObserver(),
        passwordPublishSubject.asObserver(),
        repeatPasswordPublishSubject.asObserver()).map { userName, email, password, repeatPassword in
            return self.validateRegister(userName: userName, email: email, password: password,
                                         repeatPassword: repeatPassword)
    }.startWith(false)
}

private func validateRegister(userName: String, email: String, password: String, repeatPassword: String) -> Bool {
    return userName.count >= 5 && email.isEmail() && password.count >= 5 && repeatPassword.count >= 5 && password == repeatPassword
}

我已经验证了表单,但我还需要用户接受条款和条件才能启用“下一步”按钮。

在设计响应式系统时,您需要考虑因果关系。每个可观察的链应该关注一个单一的效果,并根据需要为该效果引入尽可能多的原因。就像一个函数有几个传入的参数和 returns 一个值。

在这种情况下,你的效果是btnNext.rx.isEnabled。你必须考虑是什么导致这种影响发生变化?这是我如何编写的示例:

class View: UIView {
    weak var tvUserName: UITextView!
    weak var tvEmail: UITextView!
    weak var tvPassword: UITextView!
    weak var tvRepeatPassword: UITextView!
    weak var btnNext: UIButton!
    weak var checkBoxOutlet: UIButton!

    let disposeBag = DisposeBag()

    override func awakeFromNib() {
        super.awakeFromNib()

        isNextButtonEnabled(
            username: tvUserName.rx.text.orEmpty.asObservable(),
            email: tvEmail.rx.text.orEmpty.asObservable(),
            password: tvPassword.rx.text.orEmpty.asObservable(),
            repeatPassword: tvRepeatPassword.rx.text.orEmpty.asObservable(),
            checkBox: checkBoxOutlet.rx.tap.asObservable()
        )
        .bind(to: btnNext.rx.isEnabled)
        .disposed(by: disposeBag)
    }
}

func isNextButtonEnabled(username: Observable<String>, email: Observable<String>, password: Observable<String>, repeatPassword: Observable<String>, checkBox: Observable<Void>) -> Observable<Bool> {
    return Observable.combineLatest(
        [
            username.map { [=10=].count >= 5 },
            email.map { [=10=].isEmail() },
            password.map { [=10=].count >= 5 },
            Observable.combineLatest(password, repeatPassword) { [=10=] ==  },
            checkBox.scan(false) { state, _ in !state }.startWith(false)
        ]
    )
    .map { [=10=].allSatisfy { [=10=] } }
}

注意这里的视图模型是多么简单。这是 isNextButtonEnabled 函数。它可以很容易地进行测试,但它是如此简单,以至于您甚至可能觉得没有必要进行测试。确定是否启用下一个按钮的所有条件都集中在一个地方以便于理解。不需要一堆 PublishSubjects。它更容易阅读和理解。