私は次のコードを持っています:
func completeLoadAction(urlString:String) -> Int {
let url = URL(string:urlString.trimmingCharacters(in: .whitespaces))
let request = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
let ac = UIAlertController(title: "Unable to complete", message: "The load has been added to the completion queue. This will be processed once there is a connection.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
return
}
let httpStatus = response as? HTTPURLResponse
var httpStatusCode:Int = (httpStatus?.statusCode)!
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
let ac = UIAlertController(title: "Completed Successfully", message: "The "+coldel+" has been completed successfully", preferredStyle: .alert)
ac.addAction(UIAlertAction(title:"Continue", style: .default, handler: { action in self.performSegue(withIdentifier: "segueConfirmedLoad", sender: self) }))
self.present(ac, animated: true)
}
task.resume()
return httpStatusCode
}
これを呼び出すことができるようにする必要があります。また、httpステータスコードであるため、戻り値を確認すると、呼び出しが成功したかどうかが通知されます。
問題はdataTaskにあるためです。ここで応答ステータスコードにアクセスできません。
var httpStatusCode:Int = (httpStatus?.statusCode)!
これは、Task.Resume()が呼び出されてタスクが非同期になるまでタスクが開始されないため、動作しないためです。
これを回避する方法はありますか?
非同期パターンを使用する方法は常にあります。
関数を非同期にするには、完了ブロックを追加します
func completeLoadAction(urlString:String, completion: (Int) -> ()) {
let url = URL(string:urlString.trimmingCharacters(in: .whitespaces))
let request = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
DispatchQueue.main.async {
let ac = UIAlertController(title: "Unable to complete", message: "The load has been added to the completion queue. This will be processed once there is a connection.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
completion(0) // or return an error code
return
}
let httpStatus = response as? HTTPURLResponse
var httpStatusCode:Int = (httpStatus?.statusCode)!
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
DispatchQueue.main.async {
let ac = UIAlertController(title: "Completed Successfully", message: "The "+coldel+" has been completed successfully", preferredStyle: .alert)
ac.addAction(UIAlertAction(title:"Continue", style: .default, handler: { action in self.performSegue(withIdentifier: "segueConfirmedLoad", sender: self) }))
self.present(ac, animated: true)
}
completion(httpStatusCode)
}
task.resume()
}
そしてそれをこう呼ぶ
completeLoadAction(urlString: "www.something.com") { code in
print(code)
}
同期して待機するには、以下のようなセマフォを使用できます
struct Login {
static func execute() -> Bool {
let request = NSURLRequest....
var success = false
let semaphore = DispatchSemaphore(value: 0)
let task = URLSession.shared.dataTask(with: request, completionHandler: { _, response, error in
if let error = error {
print("Error while trying to re-authenticate the user: \(error)")
} else if let response = response as? HTTPURLResponse,
300..<600 ~= response.statusCode {
print("Error while trying to re-authenticate the user, statusCode: \(response.statusCode)")
} else {
success = true
}
semaphore.signal()
})
task.resume()
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
return success
}
}
これはすべての状況で機能するわけではありません。共有拡張機能を実装しているとします。そして、ブール値を返すisContentValid()
メソッドをオーバーライドしています(コンテンツが有効な場合はtrue)...しかし、コンテンツが有効かどうかをテストするには、サーバーが実行されていることを確認する必要があります(これは不自然な例です)。非同期のhttp呼び出し(完了ブロックかどうか)を行うと、ブール値の適切な値を返すことができません。これを実現する唯一の方法は、同期呼び出しを行い、戻り値に基づいてtrue/falseを返すことです。
この場合、セマフォパターンを投稿した答えが適切です。