daPhips daPhips - 4 years ago 1098
Swift Question

Wait for download task to finish in NSURLSession

My scenario looks like this: I have a class which has a function in it and this function makes a POST request to a server with NSURLSession. In another function which is not in that class I call that function with the task in it, which runs asynchronously I think. I'm encountering following problem: When I download the JSON response from the server and write it to a variable and return it, the variable I've written to is still nil since it didn't wait for the thread/task to finish downloading and returns the initial value which is nil.

So my question is how can I wait for that task to finish and then continue the whole program.

This is my class with the request function:

class DownloadTask {
var json:NSDictionary!
var cookie:String!
var responseString : NSString = ""

func forData(completion: (NSString) -> ()) {

var theJSONData:NSData!
do {
theJSONData = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions())
} catch _ {
print("error")
}

let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
if cookie != nil {
request.setValue(cookie, forHTTPHeaderField: "Cookie")
}
request.HTTPBody = theJSONData

let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in

if error != nil {
print("error")
return
} else {
completion(NSString(data: data!, encoding: NSUTF8StringEncoding)!)
}
}
task.resume()
}


}

And this is my calling code:

let task = DownloadTask()
task.forData { jsonString in
do {
if let json: NSDictionary = try NSJSONSerialization.JSONObjectWithData(jsonString.dataUsingEncoding(NSUTF8StringEncoding)!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary {
if json["error"] != nil {
authenticated = false
} else {
let result = json["result"] as! NSDictionary
user.personId = result["personId"] as! NSNumber
user.sessionId = result["sessionId"] as! NSString
print("test0")
authenticated = true
}
}
}
catch _ {
print("error")
}
}
print("test1")


The output is:

test1
test0


And I can't acess the variables user.sessionId after that task.

Answer Source

As I said before in the comments of the main post the download NSURLSession.sharedSession().dataTaskWithRequest(request) run synchronously to the main thread which means it downloads the data synchronously to the displaying. So in short the print("test0") will be executed when the dispatched thread is finished (aka downloaded the data). To fix this I had to use semaphores (I also combined the first code and the second code from the original question):

private func downloadData(completion: (String) -> ()) {
   let semaphore = dispatch_semaphore_create(0);
   let request = NSMutableURLRequest(URL: url)

   var theJSONData = NSData()
   do {
       theJSONData = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions())
   } catch {
       completion(String())
   }

   request.HTTPMethod = "POST"
   request.setValue("application/json", forHTTPHeaderField: "Content-Type")
   request.HTTPBody = theJSONData

   let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
       data, response, error in
       data == nil ? completion(String()) : completion(String(data: data!, encoding: NSUTF8StringEncoding)!)
       dispatch_semaphore_signal(semaphore);
   }
   task.resume()
   dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

This may not be the most efficient nor the most elegant solution but in my case I really had to wait for the downloading to be finished else it shouldn't do anything.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download