I have the following code:
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
}
I need to be able to call this and at the same time check the return value as it is the http status code, it will let me know if the call was successful or not.
Problem is because it's in a dataTask I can't access the responses status code here
var httpStatusCode:Int = (httpStatus?.statusCode)!
Because the task doesn't start until Task.Resume() is called and the task is asynchronous so it will never work.
Are there any ways around this?
There is always a way to use the asynchronous pattern.
To make the function asynchronous add a completion block
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()
}
and call it thusly
completeLoadAction(urlString: "www.something.com") { code in
print(code)
}
To make it synchronous and wait you can use semaphores such as below
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
}
}
This won't work in all situations. Suppose you are implementing a shared extension. And you are overriding the isContentValid()
method that returns a boolean (true if the content is valid)... but in order to test if the content is valid, you want to verify that the server is running (this is a contrived example). If you make an asynchronous http call--completion block or not--you cannot return the proper value of the boolean; the only way to accomplish this is to do a synchronous call and return true/false based on the return value.
The answer that posted the semaphore pattern is the proper one to use in this case.