Usar rebash al actualizar tokens basado en el código de error http

Encontré este ejemplo en Cómo actualizar token de oauth usando moya y rxswift, que tuve que modificar un poco para poder comstackr. Este código funciona al 80% para mi escenario. El problema con esto es que se ejecutará para todos los errores de http, y no solo 401 errores. Lo que quiero es pasar todos mis otros errores de HTTP como errores, para que pueda manejarlos en otro lugar y no tragarlos aquí.

Con este código, si obtengo un HttpStatus 500 , ejecutará el código de authentication 3 veces, lo cual obviamente no es lo que quiero.

He intentado modificar este código para manejar solo manejar errores 401 , pero parece que no importa lo que hago, no puedo get el código para comstackr. Siempre se queja de un tipo incorrecto de retorno, "Cannot convert return expression of type Observable<Response> to return type Observable<Response>" cual no tiene sentido para mí.

Lo que quiero: manejar 401, pero detenerme en todos los demás errores

 import RxSwift import KeychainAccess import Moya public extension ObservableType where E == Response { /// Tries to refresh auth token on 401 errors and retry the request. /// If the refresh fails, the signal errors. public func retryWithAuthIfNeeded() -> Observable<E> { return self.retryWhen { (e: Observable<ErrorType>) in return Observable.zip(e, Observable.range(start: 1, count: 3), resultSelector: { $1 }) .flatMap { i in return AuthProvider.shanetworkingInstance.request( .LoginFacebookUser( accessToken: AuthenticationManager.defaultInstance().getLoginTokenFromKeyChain(), useFaceBookLogin: AuthenticationManager.defaultInstance().isFacebookLogin()) ) .filterSuccessfulStatusCodes() .mapObject(Accesstoken.self) .catchError { error in log.debug("ReAuth error: \(error)") if case Error.StatusCode(let response) = error { if response.statusCode == 401 { // Force logout after failed attempt log.debug("401:, force user logout") NSNotificationCenter.defaultCenter().postNotificationName(Constants.Notifications.userNotAuthenticated, object: nil, userInfo: nil) } } return Observable.error(error) }.flatMapLatest({ token -> Observable<Accesstoken> in AuthenticationManager.defaultInstance().storeServiceTokenInKeychain(token) return Observable.just(token) }) } } } } 

error de compilation

¿Qué línea tiene el error de compilation? Me parece que sería esta línea:

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

Si es así, probablemente sea porque catchError espera que el bloque devuelva un Observable<Response> con el que puede continuar en caso de error, y no un Observable<ErrorType> .

En cualquier caso, ayuda a anotar tu código con más types para que puedas identificar problemas como este, además de ayudar al comstackdor Swift, que a menudo no puede descubrir este tipo de cosas por sí solo. Entonces, algo como esto te habría ayudado:

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

Tenga en count que solo le muestro cuál es el error y cómo conseguir que Xcode le brinde mejores posts de error. Lo que está intentando devolver aún no es correcto.

Solo vuelva a intentarlo en 401

No estoy seguro de por qué esperas que este código trate 401 diferente (que no sea la publicación en el centro de notifications y el logging). Tal como está, estás capturando el error, pero siempre estás devolviendo un Observable con un evento de Error al final ( return Observable.error(error) ), por lo que nunca volverá a intentarlo.

Para get 401 para reintentar, debe devolver un Observable desde el bloque de retryWhen bloquee, lo que enviará un evento Next (lo que significa que desea reintentar). Para todos los demás códigos de estado, ese Observable debe enviar un Error (como lo está haciendo actualmente), lo que significará que no desea reintentar y que desea que se propague el error.

Entonces algo como esto:

 .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) } } } 

Cuando catchError , si no es un error 401, simplemente necesita throw el error. Eso enviará el error por la tubería.