Search code examples
swiftalamofirerx-swift

RxSwift+Alamofire custom mapper error handling


RxSwift one more question about error handling:

I'm using Alamofire+RxAlamofire this way:

SessionManager.default.rx.responseJSON(.post, url, parameters:params)

example:

func login() -> Observable<Int> {
    let urlString = ...
    let params = ...
    return SessionManager.default.rx.responseJSON(.post, url, parameters:params)
        .rxJsonDefaultResponse()
        .map({ (data) in
            data["clientId"] as! Int
        })
}
....

extension ObservableType where Element == (HTTPURLResponse, Any) {

    func rxJsonDefaultResponse() -> Observable<Dictionary<String, Any>> {

        return self.asObservable().map { data -> Dictionary<String, Any> in

            if... //error chechings
                    throw NSError(domain: ..,
                                  code: ...,
                                  userInfo: ...)
            }
            ...
            return json
        }
    }
}

using:

loginBtn.rx.tap
.flatMap{ _ in
    provider.login()
}.subscribe(onNext: { id in
       ... 
    }, onError: { (er) in
        ErrorPresentationHelper.showErrorAlert(for: er)
    })
.disposed(by: bag)

So if error occurred everything works as intended: error alert shows and 'loginBtn.rx.tap' disposed, but I need it to be still alive, what's my strategy here if I want to use onError block?


Solution

  • You can use materialize function in rxSwift. It will convert any Observable into an Observable of its events. So that you will be listening to Observable<Event<Int>> than Observable<Int>. Any error thrown from the flatmap would be captured as error event in your subscription block's onNext and can be handled there. And your subscription would still be alive. Sample code would be as follows.

            button.rx.tap.flatMap { _ in
                return Observable.just(0)
                .flatMap { _ -> Observable<Int> in
                    provider.login()
                }.materialize()
            }.subscribe(onNext: { event in
                switch event {
                case .next:
                    if let value = event.element {
                        print(value) //You will be getting your value here
                    }
                case .error:
                    if let error = event.error {
                        print(error.localizedDescription) //You will be getting your captured error here
                    }
                case .completed:
                    print("Subscription completed")
                }
            }) {
                print("Subscription disposed")
            }.disposed(by: disposeBag)
    

    Hope it helps. You can checkout the materialize extension here.