Lê Khánh Vinh
Lê Khánh Vinh

Reputation: 2611

Rxswift cancel observer when an event happen and resubcribe

How can I cancel the observable when an event happens and resubscribe (basically I want an observable to skip emitting when another event happens (Cancel button tap)).

Detailed scenario: Cancel button is tapped on a UISearchBar, how to make observable to pause and stop emitting event when the Cancel button is tapped?

I'm thinking of something like this:

Observable.of(searchClick,historyClick).merge().debounce(0.3, scheduler: scheduler)
                    .takeUntil(searchBar.rx.cancelButtonClicked)
                    .bind(to: viewModel.search)
                    .disposed(by: disposeBag)

but the subscription is stopped after that. How can we resubscribe or any other way to skip emitting when an event happens?

Upvotes: 0

Views: 2475

Answers (2)

K.pen
K.pen

Reputation: 319

Utility Function for Cancelling and Resubscribing:

func cancelAndResubscribe<T>(
    observable: Observable<T>, 
    cancelTrigger: PublishSubject<Void>, 
    resubscribeTrigger: PublishSubject<Void>, 
    disposeBag: DisposeBag
) {
// Initial subscription to the observable
observable
    .takeUntil(cancelTrigger) // Unsubscribe when cancelTrigger emits
    .subscribe(onNext: { value in
        print("Received value: \(value)")
    })
    .disposed(by: disposeBag)

// Handle resubscription logic
resubscribeTrigger
    .subscribe(onNext: {
        print("Resubscribing...")
        // Cancel the current subscription if it exists
        cancelTrigger.onNext(())
        cancelTrigger.onCompleted()

        // Resubscribe to the observable
        observable
            .takeUntil(cancelTrigger)
            .subscribe(onNext: { value in
                print("Received value after resubscription: \(value)")
            })
            .disposed(by: disposeBag)
    })
    .disposed(by: disposeBag)
}

Usage Example:

import RxSwift

let disposeBag = DisposeBag()
let cancelTrigger = PublishSubject<Void>()
let resubscribeTrigger = PublishSubject<Void>()

// Observable you want to subscribe to
let observable = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)

// Call the utility function
cancelAndResubscribe(observable: observable, cancelTrigger: cancelTrigger, resubscribeTrigger: resubscribeTrigger, disposeBag: disposeBag)

// Trigger to cancel after 5 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    cancelTrigger.onNext(())
}

// Trigger to resubscribe after 10 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
    resubscribeTrigger.onNext(())
}

Explanation:

  1. cancelAndResubscribe is a utility function that takes an observable, a cancel trigger, and a resubscribe trigger to manage the lifecycle of the subscription.
  2. The function subscribes to the observable and cancels it when cancelTrigger emits using takeUntil.
  3. When resubscribeTrigger emits, it cancels the current subscription and resubscribes to the observable, reactivating the flow.

Upvotes: -1

Maxim Volgin
Maxim Volgin

Reputation: 4077

I suppose it is better to switch to the next observable (which may be dormant at the time of the switch) using '.switchLatest()' operator.

let eventSwitch: BehaviorSubject<Observable<T>> = BehaviorSubject.create(/*initial value*/) // where T is your type of choice
let events = eventSwitch.switchLatest() // subscribe consumer to this
eventSwitch.onNext(/*new event source of type Observable<T>*/) // call this from "Cancel" button

UPDATE

let eventSwitch = BehaviorSubject(value: Observable
    .of(searchClick,historyClick)
    .merge()
    .debounce(0.3, scheduler: scheduler)
  )
let events = eventSwitch.switchLatest()

events
  .bind(to: viewModel.search)
  .disposed(by: disposeBag)

// call this from "Cancel" button
eventSwitch.onNext(...)
// argument can be empty observable or 'Observable.of(searchClick,historyClick).merge().debounce(0.3, scheduler: scheduler)'

Upvotes: 0

Related Questions