Search code examples
iosswiftreactive-programmingrx-swift

How does observer.onComplete() works without onNext in chain of flatmap? - does it have to skip all the following flatmaps?


See the following code snippet:

 
class ViewController: UIViewController {

    let disposeBag = DisposeBag()
    @IBOutlet weak var clickMe: UIButton!

    override func viewDidLoad() {
    super.viewDidLoad()

        onAction1()
        onAction2()
    }
    func onAction1(){
        Observable.just(4)
        .flatMapLatest{self.performTask(value: $0)}
        .subscribe(onNext:{ data in
            print("data is")
            print(data)
        }, onCompleted: {
            print("task completed")
        })
        .disposed(by: disposeBag)
    }
    func onAction2(){
        clickMe.rx.tap
        .map{4}
        .flatMapLatest{self.performTask(value: $0)}
        .subscribe(onNext:{ data in
            print("data is")
            print(data)
        }, onCompleted: {
            print("task completed")
        })
        .disposed(by: disposeBag)
    }

    func performTask(value:Int) -> Observable<Int>{

        return self.skipObservable(value:value)
          // need to skip the following flatmap when value is 4 
        .flatMapLatest{self.multipierObservable(value: $0)} 

       
    }

    func multipierObservable(value:Int) -> Observable<Int>{
        return Observable.just(value*value)
    }
    func skipObservable(value:Int) -> Observable<Int>{

        return Observable.create { (observer:AnyObserver<Int>) -> Disposable in
            print("value is", value)
            if value == 4{
                observer.onCompleted()
            }
            else {
                observer.onNext(value)
                observer.onCompleted()
            }


            return Disposables.create()

        }

    }


}

Sample Output

value is 4
task completed

/// output on button tap. Not printing 'task completed' - why?
value is 4

I can see for action1, the observer.onComplete without observer.onNext of first skipObservable causes the observer to go completed state skipping the second flatmap.

But when same set of flatmap is being called using a button tap, the state of the observer is not getting completed.

What is the difference between the two?


Solution

  • TL;DR - The onAction2() doesn't complete because it's waiting to see if any more button taps are going to occur.


    In onAction1() you are initiating the Observable chain with a just call. The just operator emits a value and then emits a completed event.

    In onAction2() you are initiating the Observable chain with a button which emits next events when tapped but doesn't emit a completed event until the button goes out of scope.

    In both cases, you then route the event into a flatMapLatest call. One of the properties that flatMap Observables have is that they don't complete until all of the Observables they are subscribed to complete. In this case that's both the latest Observable that it created when it was triggered as well as the source observable.

    In both cases, the Observable that the flatMap creates when triggered complete, but the source doesn't complete in the onAction2 case (as explained above) so the flatMap doesn't complete in that case. It's waiting to see if any more button taps are going to occur.