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
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:
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With