【问题标题】:Rx Swift : Complex TextFields ValidationRx Swift:复杂的文本字段验证
【发布时间】:2026-01-18 18:10:01
【问题描述】:

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

我正在尝试对我的文本字段进行表单验证。 我的自定义 TextField 类有一个方法 isValid() 和一个 regexpisValid 返回基于 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
})

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

【问题讨论】:

    标签: ios swift rx-swift


    【解决方案1】:

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

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

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

    然后您可以将所有文本字段中的isTextValidcombineLatest 结合起来。


    您还可以从自定义文本字段中提取验证逻辑(也许您甚至根本不需要自定义文本字段)。

    好处:

    • 验证可能更容易进行单元测试
    • 您可以轻松地在应用的不同位置重复使用验证(例如,如果您使用过 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
            }
        }       
    }
    

    那么你可以如下使用它:

    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
    

    现在,如果您想为验证器编写单元测试(您可能应该这样做),您可以简单地将 Observable.just("Some value") 作为 input 传递给 TextValidator 并验证 validate() 返回的 Observable 做了什么。

    【讨论】:

      【解决方案2】:

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

      见:

      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)
      

      【讨论】:

      • 你怎么能在其中添加两个字段是否匹配(密码)?
      • @LeeProbert 取决于您是否要在规则一致性之前或之后检查相等性