Home > database >  RxSwift, RxCocoa - no called when writing TextField after validation
RxSwift, RxCocoa - no called when writing TextField after validation

Time:09-27

I am new to RxSwift and RxCocoa I need to any advice for learning

After result of Checking Id Validation, expect no word in label

But it is updating label and no entering in break point at bind function

What’s problem my code…?


  var disposeBag: DisposeBag = DisposeBag()
  let viewModel = ViewModel()

  override func viewDidLoad() {
        super.viewDidLoad()

        let input: Signal<String> = userIDTextField.rx.text.orEmpty
            .asSignal(onErrorSignalWith: .empty())
        let output: Driver<String> = viewModel.bind(input)
        
        disposeBag.insert(
            output.drive(userIDLabel.rx.text)
        )
  }
struct ViewModel {

  func checkUserIDFromDB(id: String) -> Signal<Bool> {
        return .just(false).asSignal()
  }
    
  func bind(_ input: Signal<String>) -> Driver<String> {
        let validState = input
            .map { _ in self.checkUserIDFromDB(id:)}
            .withLatestFrom(input)
        
        return validState.asDriver(onErrorDriveWith: .empty())
  }
}

CodePudding user response:

This line: .map { _ in self.checkUserIDFromDB(id:)} produces a Signal<(String) -> Signal<Bool>> which is likely not what you wanted.

I'm going to assume that the goal here is to pass the entered string to the network request and wait for it to emit. If it emits true then emit the string to the label, otherwise do nothing...

Further, let's simplify things by using the Observable type instead of Signals and Drivers:

final class ViewController: UIViewController {
    let userIDTextField = UITextField()
    let userIDLabel = UILabel()
    let disposeBag = DisposeBag() // this should be a `let` not a `var`
    let viewModel = ViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        let input = userIDTextField.rx.text.orEmpty
        let output = viewModel.bind(input.asObservable())
        disposeBag.insert(
            output.bind(to: userIDLabel.rx.text)
        )
    }
}

struct ViewModel {
    func checkUserIDFromDB(id: String) -> Observable<Bool> { .just(false) }

    func bind(_ input: Observable<String>) -> Observable<String> {
        input.flatMapLatest { id in  // Note this should be `flatMapLatest` not `map`
            Observable.zip(  // zip up the text with its response
                Observable.just(id),
                self.checkUserIDFromDB(id: id) // you weren't actually making the network call. This makes it.
                    .catchAndReturn(false) // if the call fails, emit `false`.
            )
        }
        .compactMap { $0.1 ? $0.0 : nil } // if the response is true, emit the text, else nothing
    }
}

The biggest concern I have with this code is what happens if the user continues to type. This will fire after every character the user enters which could be a lot of network requests, the flatMapLatest will cancel ongoing requests that are no longer needed, but still... Consider putting a debounce in the stream to reduce the number of requests.

Learn more about the various versions of flatMap from this article.

  • Related