Search code examples
iosswiftrx-swiftrx-cocoa

RxSwift: Deliver the first item immediately, debounce following items


I have a textfield to validate, I want to disable a button whenever user is typing. After user stops typing (debounce by 1 second), the validation is carried out and the button is conditionally enabled based on the result. Notice the corner case when user typed only one character, validation should still happen.

--"a"-"ab"-"abc"------------------"ab"--"a"------------------"ab"-----------------

--false---------validate("abc")---false----validate("a")-----false--validate("ab")

This SO (Deliver the first item immediately, 'debounce' following items) proposes the following solution in RxJava. But it seems only returns the very first element, not when user starts typing again after debounce? Correct me if I am wrong

Observable.from(items).publish(publishedItems -> 
    publishedItems.limit(1).concatWith(
        publishedItems.skip(1).debounce(1, TimeUnit.SECONDS)
    )
)

Solution

  • After some thinking, I was able to fully solve it in the following way, everything works as I intended

        let input = textField.rx.text.distinctUntilChanged()
        let keystroke = input.map { _ in Observable.just(false) }
        let validate = input
            .flatMap { Observable.from(optional: $0) }  // 1  
            .filter { $0.count >= minimumTextLength }      
            .debounce(1, scheduler: MainScheduler.instance)
            .map { self.networkManager.validate($0).asObservable() } // 2
        return Observable.merge(keystroke, validate).switchLatest().distinctUntilChanged() // 3
    
    1. Safely unwrap optional strings
    2. validate returns Single<Bool> in my case, so I turn it into Observable. Notice I intentionally used map not flatMap in order to use the functionality switchLatest provides in step 3
    3. I merge a and b to create an Observable<Observable<Bool>>, switchLatest allows me to ignore the validate result if user starts typing again. distinctUntilChanged discards repeated falses