iOS rxSwift : retryWhen updating refresh token

标签 ios swift rx-swift

我有一个调用网络服务的静态函数。 当 400 响应代码发生时,我想重做网络调用。

当前代码可以正常工作,除了 header 中的 refreshToken 不会在一次又一次尝试之间更新。

我认为问题是因为创建了 Observable 但 请求函数 在重试时没有更新。

我在网上说我应该在 Observable 上使用延迟方法,但我不知道该怎么做。

我已经尝试移动代码:headers = [HeaderKeys.refreshToken.rawValue: "test test"] 任何地方,但它仍然不会使用“测试测试”刷新 token 进行调用。它总是使用旧的。

我该如何解决这个问题?

 static func getAccessToken() -> Observable<GetAccessTokenResponse> {
            var retryCounter = 0
        let maxRetryCounter = 3
        let delayRetry = 10.0

        guard let refreshToken = NetworkHelper.shared.refreshToken else {
            return Observable.error(AuthenticationError.networkError)
        }

        var headers = [HeaderKeys.refreshToken.rawValue: refreshToken]


        return NetworkHelper.shared
            .request(url: CoreAPI.accessToken.url, request: nil, headers: headers, responseType: GetAccessTokenResponse.self, method: .get, encoding: nil)
            .catchError({ (error) -> Observable<(GetAccessTokenResponse?, Int)> in
                return Observable.error(AuthenticationError.networkError)
            })
            .flatMap({ (response) -> Observable<GetAccessTokenResponse> in
                // check http status code
                switch response.1 {
                case 200:
                    guard response.0?.accessToken != nil else {
                        return Observable.error(AuthenticationError.genericError)
                    }
                    // success
                    return Observable.just(response.0!)
                case 400:
                    // invalid parameters, refresh token not existing
                    return Observable.error(AuthenticationError.invalidParameters)
                case 404:
                    // user not existing
                    return Observable.error(AuthenticationError.userDoesntExist)
                default:
                    // by default return network error
                    return Observable.error(AuthenticationError.networkError)
                }
            })
            .retryWhen({ (errors) -> Observable<Void> in
                return errors
                    .do(onNext: { (error) in
                        headers = [HeaderKeys.refreshToken.rawValue: "test test"]
                    })
                    .flatMap({error -> Observable<Int> in
                        debugLog("Retrying get refresh token")
                        if retryCounter >= maxRetryCounter {
                            let authError = error as? AuthenticationError ?? .genericError
                            if authError == AuthenticationError.invalidParameters {
                                // publish logged false on subject
                                VDAAuthenticationManager.shared.logged.onNext(false)
                            }
                            return Observable.error(error)
                        }
                        // increase the retry counter and retry
                        retryCounter += 1
                        return Observable<Int>.timer(delayRetry, scheduler: MainScheduler.instance)
                })
                .flatMap ({ (_) -> Observable<Void> in
                    return Observable.just(())
                })
            })
    }

最佳答案

文章中RxSwift and Retrying a Network Request Despite Having an Invalid Token我解释了如何保留和更新 token 以及如何在收到 401 错误时处理重试。使用 deferred 是部分答案。

在您的特定情况下。看起来您可以像这样使用我的服务:

func getToken(lastResponse: GetAccessTokenResponse?) -> Observable<(response: HTTPURLResponse, data: Data)> {
    guard let refreshToken = lastResponse?.refreshToken else { return Observable.error(AuthenticationError.networkError) }
    var request = URLRequest(url: CoreAPI.accessToken.url)
    request.addValue(refreshToken, forHTTPHeaderField: HeaderKeys.refreshToken.rawValue)
    return URLSession.shared.rx.response(request: request)
}

func extractToken(data: Data) throws -> GetAccessTokenResponse {
    return try JSONDecoder().decode(GetAccessTokenResponse.self, from: data)
}

let tokenService = TokenAcquisitionService(initialToken: nil, getToken: getToken, extractToken: extractToken(data:))

在上面,你将必须传递一个有效的 initialToken 而不是 nil 或者你将必须修改 getToken 以便它可以获取 token ,即使它没有'有一个刷新 token 。

下面是一个如何使用 deferred 的例子:

let response = Observable
    .deferred { tokenAcquisitionService.token.take(1) }
    .flatMap { makeRequest(withToken: $0) }
    .map { response in
        guard response.response.statusCode != 401 else { throw ResponseError.unauthorized }
        return response
    }
    .retryWhen { $0.renewToken(with: tokenAcquisitionService) }

我在文章中解释了每一行代码的用途以及它是如何工作的。

关于iOS rxSwift : retryWhen updating refresh token,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54597414/

相关文章:

ios - 我可以在单击按钮时以编程方式更改 Localizable.strings 文件而不重新启动应用程序吗

ios - 避免嵌套 Rx 订阅调用 - 原因是什么?

ios - RxSwift 在关闭时正确处理订阅

swift - 从 NSObject 继承的对象导致错误 Property 'self._username' not initialized at super.init call

ios - 使用来自 RxSwift 变量的 searchBar 过滤 collectionView

ios - Objective-C/iOS 内存管理问题

ios - 使用 swift 从应用程序委托(delegate)打开 View Controller

ios - 迦太基上的 UrabanAirship

ios - 使用 NavigationViewController 导航

macos - viewWillDisappear 会被 Cmd+W 调用,但不会被 Cmd+Q 调用(对于 Swift 制作的 Mac 应用程序)