Rx Swift:复杂的TextFields验证

CZ5*_*Z54 3 ios swift rx-swift

我是新手,RxSwift我发现的所有例子都处理简单的案例.

我正在尝试为我的文本字段进行表单验证.我的自定义TextField类有一个方法isValid()和一个regexp.该isValid收益是基于regexp属性.

到目前为止,我写了以下内容:

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
})
Run Code Online (Sandbox Code Playgroud)

有没有人知道如何更新代码Observable<Bool>基于我的isValid()方法而不是text!.characters.count

Mic*_*uba 5

可能有很多方法可以做到这一点.

您可以使用在自定义TextField类中filter转换rx.textObservable:

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

然后你可以isTextValid从所有文本字段组合combineLatest.


您还可以从自定义文本字段中提取验证逻辑(可能根本不需要自定义文本字段).

优点:

  • 验证可以更容易进行单元测试
  • 您可以轻松地在应用程序的不同位置重用验证(例如,UITextView如果您曾经使用它).

验证者类的草稿:

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
        }
    }       
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用它如下:

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
Run Code Online (Sandbox Code Playgroud)

现在,如果你想要写的验证单元测试(这你应该这样做),你可以简单地传递Observable.just("Some value")作为inputTextValidator和验证什么可观测的返回validate()呢.


CZ5*_*Z54 4

我自己找到了答案。问题出在第一个地图中,我不应该使用匿名参数。

看 :

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)
Run Code Online (Sandbox Code Playgroud)