Using retryWhen to update tokens based on http error code Using retryWhen to update tokens based on http error code ios ios

Using retryWhen to update tokens based on http error code


Compilation Error

Which line has the compilation error? It seems to me that it would be this line:

.catchError {    error in    //...    return Observable.error(error)  // is this the line causing the compilation error?}

If so, it's probably because catchError is expecting the block to return an Observable<Response> with which it can continue in case of an error, and not an Observable<ErrorType>.

In either case, it helps to annotate your code with more types so that you can pinpoint problems like this, as well as help the Swift compiler, which often can't figure out these kinds of things on its own. So something like this would have helped you:

.catchError {    error -> Observable<Response> in    //...    return Observable.error(error)  // Swift should have a more accurate and helpful error message here now}

Note that I'm only showing you what the error is and how to get Xcode to give you better error messages. What you're trying to return still isn't correct.

Only retry on 401

I'm not sure why you're expecting this code to treat 401 differently (other than posting to the notification center and logging). As it is, you're catching the error, but you're always returning an Observable with an Error event at the end (return Observable.error(error)), so it will never retry.

To get 401 to retry, you should return an Observable from the retryWhen block, which will send a Next event (signifying that you want to retry). For all other status codes, that Observable should send an Error (as you're currently doing), which will signify that you don't want to retry, and that you'd like the error propagated.

So something like this:

.retryWhen { errorObservable -> Observable<ErrorType> in    log.debug("ReAuth error: \(error)")    if case Error.StatusCode(let response) = error where response.statusCode == 401 {        log.debug("401:, force user logout")        NSNotificationCenter.defaultCenter().postNotificationName(Constants.Notifications.userNotAuthenticated, object: nil, userInfo: nil)        // If `401`, then return the `Observable<ErrorType>` which was given to us        // It will emit a `.Next<ErrorType>`        // Since it is a `.Next` event, `retryWhen` will retry.        return errorObservable    }    else {        // If not `401`, then `flatMap` the `Observable<ErrorType>` which        // is about to emit a `.Next<ErrorType>` into        // an `Observable<ErrorType>` which will instead emit a `.Error<ErrorType>`.        // Since it is an `.Error` event, `retryWhen` will *not* retry.        // Instead, it will propagate the error.        return errorObservable.flatMap { Observable.error($0) }    }}


When you catchError, if it isn't a 401 error, then you simply need to throw the error. That will send the error down the pipe.


There's a different solution to solve this problem without using Observable. It's written on pure RxSwift and returns a classic error in case of fail.

The easy way to refresh session token of Auth0 with RxSwift and Moya

The main advantage of the solution is that it can be easily applicable for different services similar to Auth0 allowing to authenticate users in mobile apps.