在 Swift 中收到来自 API 的响应之前,是否有停止流程的方法?
Is there an method to stop the flow until I get response from API in Swift?
基本上,我们确实有一个 APIHelper class,其中实现了 GET 和 POST 方法,可以从任何视图控制器调用,我们将发送一个header 中的安全访问令牌,一旦该访问令牌过期,我们需要调用 API 来获取访问令牌,并且需要在 header.[=14 中发送更新的令牌=]
class func postMethod(methodName: String, success: @escaping (AnyObject?, String?)->Void, Failure:@escaping (NSError)->Void)
{
do {
if ReachabilityManager.shared.isConnectedToNetwork() == false
{
PageNavigation.moveToInternet()
} else {
let session = URLSession.shared
let urlPath = URL(string: "\(AppConstants.BaseUrl)" + "\(methodName)")
var request = URLRequest(url: urlPath! as URL)
print( "JSON request is \(urlPath!)")
request.httpMethod = "POST"
request = WebAPIHelper.headers(methodType: "POST", methodName: methodName, request: request, isAddUserAuthorization: false)
let jsonBody = try JSONEncoder().encode(CommonDBHelper.getActiveUserRefreshToken())
request.httpBody = jsonBody
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
if(error == nil)
{
do
{
if let httpResponse = response as? HTTPURLResponse
{
let headers = httpResponse.allHeaderFields
let statusCode = httpResponse.statusCode
if (statusCode == 200)
{
let JSON = try JSONSerialization.jsonObject(with: data!, options:JSONSerialization.ReadingOptions.allowFragments)
if let currentServerTime = headers["Date"] as? String
{
success(JSON as AnyObject?, currentServerTime)
}
else{
success(JSON as AnyObject?, nil) // Closure being called as a function
}
}
else
{
GenericMethods.showAlert(alertMessage: "Some error occured. Please try again later")
}
}
else
{
GenericMethods.showAlert(alertMessage: "Some error occured. Please try again later")
}
}
catch let JSONError as NSError
{
Failure(JSONError as NSError)
print("JSON Error \(JSONError)")
GenericMethods.showAlert(alertMessage: "Some error occured. Please try again later")
}
}
else
{
Failure(error! as NSError)
GenericMethods.showAlert(alertMessage: "Some error occured. Please try again later")
}
})
task.resume()
}
}
catch {
print("Error in catch \(error.localizedDescription)")
}
}
所以 GET & POST 将同时被许多线程调用,那么如何让所有调用保持等待模式,直到我从访问令牌 API 收到响应,有没有此类场景的解决方案?
class func headers(methodType:String, methodName:String, request:URLRequest, isAddUserAuthorization : Bool) -> URLRequest
{
let methodName = methodName
var request = request
if isTokenExpired(){
//how to keep waiting all other api calling in waiting mode here until i get response
RefreshTokenAPI(completed: { (accesstoken) ->Void in
request.addValue(accesstoken, forHTTPHeaderField: "AccessToken")
})
}
else{
request.addValue(getAccessTokenFromDefaults(), forHTTPHeaderField: "AccessToken")
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
print(request.allHTTPHeaderFields!)
return request as URLRequest
}
这里的目标是在您收到访问令牌后发起后续请求。人们通常会采用一种完成处理程序模式,例如仅在检索访问请求后调用的闭包:
class func headers(methodType: String, methodName: String, request: URLRequest, isAddUserAuthorization: Bool, completion: @escaping (URLRequest) -> Void) {
var request = request
// presumably you have code that is using methodType and methodName, too ...
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
if isTokenExpired() {
refreshTokenAPI { accesstoken in
request.addValue(accesstoken, forHTTPHeaderField: "AccessToken")
completion(request)
}
} else {
request.addValue(getAccessTokenFromDefaults(), forHTTPHeaderField: "AccessToken")
completion(request)
}
}
然后而不是:
request = WebAPIHelper.headers(methodType: "POST", methodName: methodName, request: request, isAddUserAuthorization: false)
...
// currently issuing request here
调用者将根据 headers
完成处理程序中的 request
移动所有内容:
WebAPIHelper.headers(methodType: "POST", methodName: methodName, request: request, isAddUserAuthorization: false) { request in
// issue request here
...
}
// but not here
这是简单的“检索访问令牌后启动请求”模式。
您还可以追求其他模式。例如,您可以为授权请求创建一个队列,该队列将被暂停,直到 RefreshTokenApi
恢复该队列。然后,所有需要令牌的请求都将添加到该队列中。或者您可以使用自定义 Operation
模式,其中 isReady
在成功检索令牌时设置。剥猫皮的方法有很多,但希望这能说明我们采用异步模式而不是“停止”或“等待”的想法。
如果您想确保允许并发请求调用 refreshTokenAPI
,那么您可以让它等待,但只能在某些后台队列上执行此操作,并避免阻塞调用线程:
private var queue = DispatchQueue(label: "com.domain.app.token") // custom serial queue to avoid blocking calling thread
func headers(methodType: String, methodName: String, request: URLRequest, isAddUserAuthorization: Bool, completion: @escaping (URLRequest) -> Void) {
queue.async { [self] in
var request = request
// presumably you have code that is using methodType and methodName, too ...
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
if isTokenExpired() {
let semaphore = DispatchSemaphore(value: 0)
refreshTokenAPI { token in
request.addValue(token, forHTTPHeaderField: "AccessToken")
DispatchQueue.main.async { completion(request) }
semaphore.signal()
}
semaphore.wait()
} else {
request.addValue(getAccessTokenFromDefaults(), forHTTPHeaderField: "AccessToken")
DispatchQueue.main.async { completion(request) }
}
}
}
坦率地说,我可能会考虑将这些网络请求包装在 Operation
的自定义异步子类中(或使用 Combine),但这超出了这个问题的范围。
基本上,我们确实有一个 APIHelper class,其中实现了 GET 和 POST 方法,可以从任何视图控制器调用,我们将发送一个header 中的安全访问令牌,一旦该访问令牌过期,我们需要调用 API 来获取访问令牌,并且需要在 header.[=14 中发送更新的令牌=]
class func postMethod(methodName: String, success: @escaping (AnyObject?, String?)->Void, Failure:@escaping (NSError)->Void)
{
do {
if ReachabilityManager.shared.isConnectedToNetwork() == false
{
PageNavigation.moveToInternet()
} else {
let session = URLSession.shared
let urlPath = URL(string: "\(AppConstants.BaseUrl)" + "\(methodName)")
var request = URLRequest(url: urlPath! as URL)
print( "JSON request is \(urlPath!)")
request.httpMethod = "POST"
request = WebAPIHelper.headers(methodType: "POST", methodName: methodName, request: request, isAddUserAuthorization: false)
let jsonBody = try JSONEncoder().encode(CommonDBHelper.getActiveUserRefreshToken())
request.httpBody = jsonBody
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
if(error == nil)
{
do
{
if let httpResponse = response as? HTTPURLResponse
{
let headers = httpResponse.allHeaderFields
let statusCode = httpResponse.statusCode
if (statusCode == 200)
{
let JSON = try JSONSerialization.jsonObject(with: data!, options:JSONSerialization.ReadingOptions.allowFragments)
if let currentServerTime = headers["Date"] as? String
{
success(JSON as AnyObject?, currentServerTime)
}
else{
success(JSON as AnyObject?, nil) // Closure being called as a function
}
}
else
{
GenericMethods.showAlert(alertMessage: "Some error occured. Please try again later")
}
}
else
{
GenericMethods.showAlert(alertMessage: "Some error occured. Please try again later")
}
}
catch let JSONError as NSError
{
Failure(JSONError as NSError)
print("JSON Error \(JSONError)")
GenericMethods.showAlert(alertMessage: "Some error occured. Please try again later")
}
}
else
{
Failure(error! as NSError)
GenericMethods.showAlert(alertMessage: "Some error occured. Please try again later")
}
})
task.resume()
}
}
catch {
print("Error in catch \(error.localizedDescription)")
}
}
所以 GET & POST 将同时被许多线程调用,那么如何让所有调用保持等待模式,直到我从访问令牌 API 收到响应,有没有此类场景的解决方案?
class func headers(methodType:String, methodName:String, request:URLRequest, isAddUserAuthorization : Bool) -> URLRequest
{
let methodName = methodName
var request = request
if isTokenExpired(){
//how to keep waiting all other api calling in waiting mode here until i get response
RefreshTokenAPI(completed: { (accesstoken) ->Void in
request.addValue(accesstoken, forHTTPHeaderField: "AccessToken")
})
}
else{
request.addValue(getAccessTokenFromDefaults(), forHTTPHeaderField: "AccessToken")
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
print(request.allHTTPHeaderFields!)
return request as URLRequest
}
这里的目标是在您收到访问令牌后发起后续请求。人们通常会采用一种完成处理程序模式,例如仅在检索访问请求后调用的闭包:
class func headers(methodType: String, methodName: String, request: URLRequest, isAddUserAuthorization: Bool, completion: @escaping (URLRequest) -> Void) {
var request = request
// presumably you have code that is using methodType and methodName, too ...
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
if isTokenExpired() {
refreshTokenAPI { accesstoken in
request.addValue(accesstoken, forHTTPHeaderField: "AccessToken")
completion(request)
}
} else {
request.addValue(getAccessTokenFromDefaults(), forHTTPHeaderField: "AccessToken")
completion(request)
}
}
然后而不是:
request = WebAPIHelper.headers(methodType: "POST", methodName: methodName, request: request, isAddUserAuthorization: false)
...
// currently issuing request here
调用者将根据 headers
完成处理程序中的 request
移动所有内容:
WebAPIHelper.headers(methodType: "POST", methodName: methodName, request: request, isAddUserAuthorization: false) { request in
// issue request here
...
}
// but not here
这是简单的“检索访问令牌后启动请求”模式。
您还可以追求其他模式。例如,您可以为授权请求创建一个队列,该队列将被暂停,直到 RefreshTokenApi
恢复该队列。然后,所有需要令牌的请求都将添加到该队列中。或者您可以使用自定义 Operation
模式,其中 isReady
在成功检索令牌时设置。剥猫皮的方法有很多,但希望这能说明我们采用异步模式而不是“停止”或“等待”的想法。
如果您想确保允许并发请求调用 refreshTokenAPI
,那么您可以让它等待,但只能在某些后台队列上执行此操作,并避免阻塞调用线程:
private var queue = DispatchQueue(label: "com.domain.app.token") // custom serial queue to avoid blocking calling thread
func headers(methodType: String, methodName: String, request: URLRequest, isAddUserAuthorization: Bool, completion: @escaping (URLRequest) -> Void) {
queue.async { [self] in
var request = request
// presumably you have code that is using methodType and methodName, too ...
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
if isTokenExpired() {
let semaphore = DispatchSemaphore(value: 0)
refreshTokenAPI { token in
request.addValue(token, forHTTPHeaderField: "AccessToken")
DispatchQueue.main.async { completion(request) }
semaphore.signal()
}
semaphore.wait()
} else {
request.addValue(getAccessTokenFromDefaults(), forHTTPHeaderField: "AccessToken")
DispatchQueue.main.async { completion(request) }
}
}
}
坦率地说,我可能会考虑将这些网络请求包装在 Operation
的自定义异步子类中(或使用 Combine),但这超出了这个问题的范围。