Reputation: 1407
so I can't really wrap my head around the concept of retry()
and share()
function in an RxSwift.Observable
. Well, I think I may have some ideas about what they are but a certain cases got me questioning my understanding about them. So I have this test case:
func testOnViewDidLoad_WhenError_ShouldRetry3Times() throws {
var actualRetryCount = 0
let creditCardInfoProvider: CreditCardInfoProviderBlock = {
actualRetryCount += 1
return .error(RxCocoaURLError.unknown)
}
let viewModel = createViewModel(creditCardInfoProvider: creditCardInfoProvider)
viewModel.onViewDidLoad()
XCTAssertEqual(3, actualRetryCount)
}
The class is:
final class PaymentInfoViewModel {
private(set) lazy var populateData = creditCardsRelay.asDriver()
private let creditCardsRelay = BehaviorRelay<[CreditCardInfo]>(value: [])
private let creditCardInfoProvider: () -> Observable<[CreditCardInfo]>
init(creditCardInfoProvider: @escaping () -> Observable<[CreditCardInfo]>) {
self.creditCardInfoProvider = creditCardInfoProvider
}
func onViewDidLoad() {
.....
}
}
My first question is: How come this works?
func onViewDidLoad() {
Observable.just(())
.flatMapLatest { [weak self] () -> Observable<[CreditCardInfo]> in
guard let `self` = self else { return .empty() }
return self.creditCardInfoProvider() }
.retry(3)
.do(onError: { [weak self] in
self?.handleErrors(error: $0)
})
.bind(to: creditCardsRelay)
.disposed(by: disposeBag)
}
But this doesn't (the result was one. Meaning it didn't get retried.)?
func onViewDidLoad(){
creditCardInfoProvider()
.retry(3)
.do(onError: { [weak self] in
self?.handleErrors(error: $0)
})
.bind(to: creditCardsRelay)
.disposed(by: disposeBag)
}
My second question is: I have another relay that will be triggered by the same function. So I refactored it to be a shared observable like this:
func onViewDidLoad() {
let sharedCardProvider = Observable.just(())
.flatMapLatest { [weak self] () -> Observable<[CreditCardInfo]> in
guard let `self` = self else { return .empty() }
return self.creditCardInfoProvider() }
.retry(3)
.do(onError: { [weak self] in
self?.handleErrors(error: $0)
}).share()
sharedCardProvider
.bind(to: creditCardsRelay)
.disposed(by: disposeBag)
sharedCardProvider
.map { !$0.isEmpty }
.bind(to: addCreditCardButtonHiddenRelay)
.disposed(by: disposeBag)
}
The thing is, the test becomes red with the result of actualRetryCount
is 6. Deleting the share()
function returned the same value (ie. 6 retries). So, that means it got called twice like a normal observable, not a shared one. Why does that happen?
For now, what I did was putting the emitting of the second relay in a .do(onNext:)
block so that's not really the problem here. I am just confused about the behavior.
Thanks in advance.
Upvotes: 1
Views: 357
Reputation: 33967
The first one calls creditCardInfoProvider()
three times, while the second one only calls it once.
Remember, the retry only resubscribes which will cause the Observable to execute its observer closure again. In the first function, the retry is resubscribing to the Observable that just
returned whereas the second function, the retry is resubscribing to the Observable that error
returned.
If you did this instead:
let creditCardInfoProvider: CreditCardInfoProviderBlock = {
return Observable.create { observer in
actualRetryCount += 1
observer.onError(RxCocoaURLError.unknown)
return Disposables.create()
}
}
Then the closure that is passed to create
would get called every time a new subscription is made and you would see actualRetryCount
increment as expected.
As for how share
works... In order to understand share, you first have to understand hot vs cold observables.
A hot observable shares its events with multiple subscribers whereas a cold observable does not.
For example:
let random = Observable<Int>.create { observer in
observer.onNext(Int.random(in: 0..<1000))
observer.onCompleted()
return Disposables.create()
}
The above is a cold observable (which is the default.) This means that every subscriber will get a different number emitted to it. If this was a network call, then every subscriber would cause a new network request.
If you want multiple subscribers to get the same data, you need to share
the observable...
Upvotes: 1