Rx Swift : Complex TextFields Validation

我的未来我决定 提交于 2019-12-14 01:14:05

问题


I'm new to RxSwift and all example I found are handling simple cases.

I'm trying to do form validation for my textfields. My custom TextField class has a method isValid() and a regexp. The isValid return is based on the regexp attribute.

So far, I have written the following :

let valids = [mLastName, mFirstName, mEmailField].map {
    $0.rx.text.map {
        text -> Bool in
        // I want more complex logic here
        // Like return field.isValid()
        return text!.characters.count > 0
    }
}    

let _ = Observable.combineLatest(valids) { iterator -> Bool in
    return iterator.reduce(true, { $0 && $1 })
}.subscribe(onNext: { allValid in
    ///update button according to AllValid
})

Does anyone know how to update the code to base the first Observable<Bool> be based on my isValid() method instead of text!.characters.count


回答1:


There are probably many ways to do that.

You can use filter to transform rx.text Observable in your custom TextField class:

var isTextValid: Observable<Bool> {
    return rx.text.filter { _ in 
        return self.isValid()
    }
}

then you can combine isTextValid from all text fields with combineLatest.


You can also extract the validation logic from the custom text field (maybe you don't even need a custom text field at all).

Benefits:

  • validation could be easier to unit test
  • you can easily reuse validation in different places in your app (e.g. for UITextView if you ever use it).

The draft of a validator class:

class TextValidator {
    var input: Observable<String>
    var regex: NSRegularExpression

    init(input: Observable<String>, regex: NSRegularExpression) {
       self.input = input
       self.regex = regex
    }

    func validate() -> Observable<Bool> {
        return input.map { text in 
             //return true if regex matches the text
        }
    }       
}

Then you can use it as follows:

let mailValidator = TextValidator(input: mLastName.rx.text, regex: /* actual regex*/)
let firstNameValidator = TextValidator(input: mFirstName.rx.text, regex: ...)



let _ = Observable.combineLatest(mailValidator.validate(), firstName.validate(), ...) 
// and so on

Now if you want to write unit tests for the validators (which you probably should do), you can simply pass Observable.just("Some value") as input to TextValidator and verify what Observable returned by validate() does.




回答2:


I found the answer myself. The problem was in the first map, I shouldn't use anonymous parameter.

See :

let valids = [mLastName, mFirstName, mEmailField].map { field in
    field.rx.text.map({ _ in return field.isValid() })
}

_ = Observable.combineLatest(valids) { iterator -> Bool in
    return iterator.reduce(true, { return $0 && $1 })
}.bindTo(self.mValidateButton.rx.isEnabled)


来源:https://stackoverflow.com/questions/42860384/rx-swift-complex-textfields-validation

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!