如何处理等待嵌套URLSession完成

时间:2018-12-02 19:08:09

标签: swift grand-central-dispatch nsurlsession

我有一个函数可以在URLSession上提供一层。调用此函数时,我想检查当前访问令牌是否已过期(如果已过期),我将暂停当前调用,进行调用以请求新令牌,替换钥匙串中的现有条目,然后继续执行以下操作电话。

android:gravity="center"

已存在一种用于处理钥匙串的机制,因此请假定此机制已经到位。

请求新令牌的功能看起来像

CoordinatorLayout

我希望执行程序在这里暂停

func profile(with endpoint: ProfilesEndpoint, method: HTTPMethod, body: String?, headers: [String: String]?, useAuthToken: Bool = true, completion: @escaping (Either<ProfileResponse>) -> Void) {
    var request = endpoint.request
    request.httpMethod = method.rawValue

    if let body = body {
        request.httpBody = body.data(using: .utf8)
    }

    if useAuthToken {
        if !AuthService.shared.isTokenValid {
            let group = DispatchGroup()
            group.enter()
                OAuthService.shared.requestRefreshToken()
            group.leave()
        }

        let (header, token) = AuthService.shared.createAuthHeaderForNetworkRequest()
        request.addValue(token, forHTTPHeaderField: header)
    }

    if let headers = headers {
        for (key, value) in headers {
            request.addValue(value, forHTTPHeaderField: key)
        }
    }

    execute(with: request, completion: completion)
}

但是没有。

在完成其余功能之前,我如何等待此调用完成?

2 个答案:

答案 0 :(得分:0)

添加到您的requestRefreshToken方法完成处理程序中,该处理程序将在您完成令牌请求后执行

func requestRefreshToken(_ completion: @escaping () -> Void) {

    if let refreshToken = KeychainWrapper.standard.string(forKey: "RefreshToken") {

        var postBody = "grant_type=\(refreshTokenGrantType)&"
        postBody += "client_id=\(clientId)&"
        postBody += "refresh_token=\(refreshToken)&"

        let additionalHeaders = [
            "Content-Type": "application/x-www-form-urlencoded;"
        ]

        APIClient.shared.identity(with: .token, method: .post, body: postBody, headers: additionalHeaders, useAuthToken: false) { either in

            switch either {
            case .success(let results):
                guard let accessToken = results.accessToken, let refreshToken = results.refreshToken else { 
                    completion()
                    return 
                }
                AuthService.shared.addTokensToKeyChain(tokens: ["AccessToken": accessToken, "RefreshToken": refreshToken])
            case .error(let error):
                print("Error:", error)
            }

            completion()
        }
    }
}

然后将dispatchGroup保留为关闭状态,并添加group.wait()(在调用请求方法之后)以暂停当前线程,直到组的任务完成

group.enter()
    OAuthService.shared.requestRefreshToken {
        group.leave()
    }
group.wait()

注意:您可以在完成时添加布尔参数,以检查令牌请求是否成功

答案 1 :(得分:0)

为什么不在下面而不是在上面?这似乎是NSURLProtocol的理想用法。基本上:

  • URL协议在其init方法中阻止了请求的创建,并保存了所有提供的参数。
  • URL协议分配专用的NSURLSession实例并将其存储在属性中。该会话应配置为使用协议,否则您将陷入无限循环。
  • 当协议收到startLoading()调用时,它将检查令牌的有效性,然后:
    • 在该私人会话中根据需要触发新令牌的请求。
    • 响应后,或者如果令牌仍然有效,则在该私有会话中再次触发真实请求。

采用这种方法,除了在创建新会话时需要将协议添加到会话配置中的protocolClasses之外,整个身份验证过程对应用程序基本上是透明的。

(有许多网站,包括developer.apple.com,提供了自定义NSURLProtocol子类的示例;如果您决定采用这种方法,则可能应使用其中一个示例代码项目作为一个起点。)

或者,如果您想坚持使用顶层方法,则需要停止考虑“停止”方法的执行,而应将其视为“稍后再做方法的最后一部分”。都是关于异步思考的。

基本上:

  • 将方法的最后一部分(执行实际请求的代码)分解为新的方法(或块)。
  • 如果令牌有效,请立即调用该方法。
  • 如果令牌无效,请异步获取新令牌。
  • 在令牌获取调用的完成回调中,调用该方法以执行实际请求。