Reputation: 2611
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
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:
Upvotes: -1
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