iOS rxSwift:retryWhen更新刷新令牌

iOS rxSwift:retryWhen更新刷新令牌,ios,swift,rx-swift,Ios,Swift,Rx Swift,我有一个调用网络服务的静态函数。 当400响应代码出现时,我想重做网络调用 当前代码正在工作,但标头中的refreshToken在一次尝试和另一次尝试之间不更新 我认为问题是因为创建了可观察函数,但请求函数在重试时没有更新 我在网上说,我应该在可观察对象上使用延迟方法,但我不知道如何使用 我已尝试将代码移动到任何位置:headers=[HeaderKeys.refreshttoken.rawValue:“test test”]但它从未使用“test test”刷新令牌进行调用。它总是使用旧的 我

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

当前代码正在工作,但标头中的refreshToken在一次尝试和另一次尝试之间不更新

我认为问题是因为创建了可观察函数,但请求函数在重试时没有更新

我在网上说,我应该在可观察对象上使用延迟方法,但我不知道如何使用

我已尝试将代码移动到任何位置:
headers=[HeaderKeys.refreshttoken.rawValue:“test test”]
但它从未使用“test test”刷新令牌进行调用。它总是使用旧的

我怎样才能解决这个问题

 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(())
                })
            })
    }
静态函数getAccessToken()->可观察{
var retryCounter=0
设maxRetryCounter=3
让delayRetry=10.0
guard let refreshToken=NetworkHelper.shared.refreshToken else{
返回可观察的.error(AuthenticationError.networkError)
}
var headers=[HeaderKeys.refreshtToken.rawValue:refreshtToken]
返回NetworkHelper.shared
.request(url:CoreAPI.accessToken.url,请求:nil,头:头,响应类型:GetAccessTokenResponse.self,方法:.get,编码:nil)
.catchError({(错误)->在
返回可观察的.error(AuthenticationError.networkError)
})
.flatMap({(响应)->在
//检查http状态代码
开关响应{
案例200:
防护响应.0?.accessToken!=无其他{
返回可观察的.error(AuthenticationError.genericError)
}
//成功
返回可观察的。just(response.0!)
案例400:
//参数无效,刷新令牌不存在
返回可观察的错误(AuthenticationError.invalidParameters)
案例404:
//用户不存在
返回Observable.error(AuthenticationError.UserDoesTextist)
违约:
//默认情况下返回网络错误
返回可观察的.error(AuthenticationError.networkError)
}
})
.retryWhen({(错误)->在
返回错误
.do(onNext:{(错误)在
HeaderKeys=[HeaderKeys.refreshToken.rawValue:“测试”]
})
.flatMap({error->在
调试日志(“重试获取刷新令牌”)
如果retryCounter>=maxRetryCounter{
让authError=错误为?AuthenticationError???.genericError
如果authError==AuthenticationError.invalidParameters{
//在主题上发布日志错误
VDAAuthenticationManager.shared.logged.onNext(false)
}
返回可观察错误(error)
}
//增加重试计数器并重试
retryCounter+=1
返回可观察的.timer(延迟重试,调度程序:MainScheduler.instance)
})
.flatMap({()->在
返回可观察。仅(())
})
})
}
在这篇文章中,我解释了如何保存和更新令牌,以及在出现401错误时如何处理重试。使用延迟是答案的一部分

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

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:))

我在文章中解释了每行代码的用途及其工作原理。

嗨,丹尼尔,我对你的代码和文章不太清楚。你能帮我用你的解决方案实现我的代码吗?我在上面给了你一个例子。只需将
makeRequest(withToken:)
替换为发出需要令牌的请求的函数。如果令牌错误,请确保函数发出
响应error.unauthorized
。我在获取令牌和提取令牌部分遇到一些问题。。。我只想在400发生时使用从singleton类获取的新刷新令牌重做相同的服务调用。我正在对已经获取新访问令牌的服务执行重试,但有时它是并行执行的,我只想从db获取新的更新刷新令牌以收集新的访问令牌。我想知道,这是否正确处理了两个请求同时发送并且都在401结束的情况?将发送一个或两个刷新请求?如果发送了两个刷新请求,则会出现竞争条件。是的,它可以正确处理多个情况。只需在每个请求的
renewToken
中使用相同的tokenAcquisitionService对象。只会发出一个新令牌请求,然后在获取新令牌时重试所有失败的网络请求。
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) }