I have a cold observable that may get called multiple times. This observable does an expensive task (a network request) and then completes. I would like this observable to only ever make a single network call and if I need to call it again in the future I would like to get the last emitted value.
If an observable doesn't complete (i.e. just sends a next value without a completed event) I can use the .share(replay: 1, scope: .whileConnected) function to always get the last value. Unfortunately, this doesn't work with observables that completes at the end of the request. Bellow is an example:
let disposeBag = DisposeBag()
let refreshSubject = PublishSubject<Void>()
override func viewDidLoad() {
super.viewDidLoad()
let observable = Observable<String>.create { observer in
let seconds = 2.0
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
observer.onNext("Hello, World")
observer.onCompleted() // <-- Works when commented out
}
return Disposables.create()
}
.share(replay: 1, scope: .whileConnected)
refreshSubject
.flatMap { _ in observable }
.subscribe(onNext: { response in
print("response: ", response)
})
.disposed(by: disposeBag)
}
@IBAction func refreshButtonHandler(_ sender: Any) {
refreshSubject.onNext(())
}
Every time the refreshSubject is triggered it takes 2 seconds for the Hello, World to be printed. If I remove the observer.onCompleted() line however, it only takes 2 seconds the first time and subsequently returns a cached response.
Obviously this is just an example, in the real world I would not have any control if the observable completes or not but I would like to always just replay the last value regardless.
So you don't want the cold observable to be re-subscribed to even when the refresh is triggered. In that case, this is the solution:
Observable.combineLatest(refreshSubject.startWith(()), yourColdObservable)
.map { $0.1 }
.subscribe(onNext: { val in
print("*** val: ", val)
})
Using flatMap means that the observable is re-subscribed to every time an event enters the flatMap. By using combineLatest instead, the cold observable will only be subscribed to once. The combineLatest operator will store the result of the observable internally and emit it again every time the refresh subject emits.
(No share is needed for this method.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With