Search code examples
rx-swiftrx-cocoa

Async task and multiple observers with RxSwift


I have one problem and don't know, how to fix this.

In my ViewModel I have got Observable field with value.

self.diseaseDetails = Observable<Disease>.create { (observer) -> Disposable in

            _ = SAPI.get().diseases(diseases: 5, success: { (disease) in
                observer.on(.next(disease))
                observer.on(.completed)

            }) { (failedMessage) in
                observer.on(.completed)
            }

            return Disposables.create()
        }

And get data from observable like this:

public func getSections() -> Observable<String?> {
        return self.details().map { $0.sections }
    }

    public func getDiagnostics() -> Observable<String?> {
        return self.details().map { $0.diagnostics }
    }

private func details() -> Observable<Disease> {

        return Observable.of(
            self.disease.asObservable(),
            self.diseaseDetails.take(1)
        ).merge()
    }

But, two request was made in this example. I need only one request for many subscriptions. Any idea?


Solution

  • An answer...

    In order to ensure only one network request, you need to make the Observable hot by calling .share() on it. In this case, you might also want to set it to replay so .share(replay: 1). However, keep in mind that this will mean diseaseDetails will only ever make one request and then spit out that response every time it's asked. With the way you have your code structured currently, that means no refreshing.

    An explanation...

    The way streams work is that when you subscribe to an Observable, the subscription goes up the stream all the way to the source and it calls the closure you gave to the create function. So by default, every subscription calls the closure that contains your SAPI.get()... function (this is called a "Cold" observable.) However, there are other ways of creating Observables, and ways of short-circuiting this behavior. When that happens, the Observable is considered "Hot". The .share() operator is one of those ways. When the first subscription request comes in, it will subscribe to the upstream Observable, but for any subsequent subscription request, it will merely add the new observer to its list and send any subsequent responses to the new observer. If you tell the operator to "replay: n" it will store the last n events from its upstream observable and immediately send them to new subscribers as they connect.

    And some advice...

    You are explicitly ignoring the result of your network request (the _ = SAPI.get()... code). It is highly likely that the result of that function is an object that allows you to cancel requests and as such should be used in your Disposables.create to effect the cancelation.