Search code examples
observablerx-swift

How to handle success case and failure case for multiple API calls when using Observable zip in RxSwift


I call two independent API calls using Obeservable.zip(). In this case, if the first API fails(returning some kind of error), the second API never executes inside the subscribe(onNext) closure even if it is successful. and vice versa. I still want the success case and failure case from each api call.
How can I solve this issue using Observable zip or should I use another method to achieve this?

Observable.zip(api1(), api2())
.subscribe { [weak self] in
 print($1) //if the first api call fails, Nothing prints out here.
}
.onError: {
 print("error", $0)
}
.disponseBag(by: disposebag)

Solution

  • It's not just if the first fails. If either Observable fails, the other will be disposed and your onNext will not be called.

    There are a few different ways to solve this, and they all involve turning the error event into a next event.

    You could use:

    Observable.zip(api1.materialize(), api2.materialize())
        .subscribe(
            onNext: { lhs, rhs in
                switch (lhs, rhs) {
                case let (.next(left), .next(right)):
                    print(left, right)
                case let (.error(left), .next(right)):
                    print(left, right)
                case let (.next(left), .error(right)):
                    print(left, right)
                case let (.error(left), .error(right)):
                    print(left, right)
                default:
                    return
                }
        },
        onError: { _ in /*will not get called*/ })
    

    or you if you don't care about the errors, you could convert them to Optionals:

    Observable.zip(
        api1.map(Optional.some).catch { _ in .just(nil) },
        api2.map(Optional.some).catch { _ in .just(nil) }
    )
        .subscribe(
            onNext: { lhs, rhs in
                print(lhs as Any, rhs as Any) // errors will be `nil`
            },
        onError: { _ in /*will not get called*/ })
    

    If you don't need the results of both network calls to do work, then another option would be to just keep the two requests separate:

    api1
        .subscribe(onNext: { print($0) }, onError: { print($0) })
    api2
        .subscribe(onNext: { print($0) }, onError: { print($0) })