Search code examples
iosswiftcocoarxjsrx-swift

Is there some sort of Priority operator on RxSwift?


I have a textfield that has 2 rules for validation: minimum amount of chars and alphanumerical chars.

I want to be able to represent to the user what he's doing wrong in an error label but the problem is that if I bind the textfield to both rules it can be creepy because once one rule gets approved the ui does a little flickering from the color of the separator for example changing from red to green to red because of the other validation failing.

I'd like to know if there's a way to prioritize one bind over the other. For example, this is what I currently have:

let minimumValidator 
    = inputField.textField
      .rx.text.orEmpty.map { $0.count >= 8 } // Min amount of chars is 8

minimumValidator.bind(to: inputField.rx.minimumChars)
    .disposed(by: bag)

let regexValidator 
    = inputField.textField
      .rx.text.orEmpty.map { $0.matches(regex) }

regexValidator.bind(to: inputField.rx.regex)
    .disposed(by: bag)

rx.minimumChars and rx.regex are custom binders

    var minimumChars: Binder<Bool> {
        return Binder(self.base) { control, value in
            UIView.animate(withDuration: 0.1) {
                if value {
                    control.separator.backgroundColor = .white
                    control.errorLabel.isHidden = true
                } else {
                    let error = "Needs more characters"
                    control.separator.backgroundColor = .red
                    control.errorLabel.text = error
                    control.errorLabel.isHidden = false
                }
            }
        }
    }

So my idea is to prioritize idk... let's say alphanumerical validation - so it would show error from minimum char till alphanumerical error came up, so till the user resolved the alphanumerical validation it would ignore the other stream from minimum amount of chars. I'm pretty sure i'm missing some combination of combineLatest with merge or idk.


Solution

  • Make a single Binder that uses a custom enum

    enum Validation {
        case valid
        case minimumChars
        case alphaNumeric
    }
    

    And then have each validator return a value of this enum. Then combineLatest the two validators and map the result to a single Validation and bind.

    Something like

    Observable.combineLatest(minimumValidator, regexValidator) { v1, v2 in
        // if v1 is not valid return it. Otherwise return v2
    }