Reputation: 1275
I am learning Swift but I have a Variable (Followers) of type Int in ViewDidLoad, I set the value to a default of 0 . I then do a Json Request which returns 1 array and I set the value of Followers to whatever number it is inside the Json Array which is 788 . However when I do a Print afterwards the value keeps staying at 0. This is my code and it'll make more sense
override func viewDidLoad() {
super.viewDidLoad()
var followers = 0
// Sent HTTP Request
sessionn.dataTask(with:requestt, completionHandler: {(data, response, error) in
if error != nil {
// print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let Profiles = parsedData["data"] as! [AnyObject]? {
for Profiles in Profiles {
if let follow = Profiles["followers"] as? Int {
self.followers = follow
// The value here says 788
print(self.followers)
}
}
DispatchQueue.main.async {
}
}
} catch let error as NSError {
print(error)
}
}
}).resume()
print("Followers is")
// The value here says 0 every time
print(followers)
// I have even tried
print(self.followers)
}
As you can see the followers variable is set to 0 When I do my HttpPost i can see that the Variable is set to 788 when I have print(self.followers) however outside of that for Loop when I access the variable it's value goes back to 0 . How can I correct that mistake as I am learning Swift by building an app.
Upvotes: 1
Views: 240
Reputation: 131426
You misunderstand how async code works.
When you set up an NSSession
data task, the call to task.resume()
returns immediately, before the network request has been sent out.
You should put the code that handles the response INSIDE the completion handler. Even though your The value here says 0
print statement happens after you call resume on the network task, the data has not been retrieved yet.
Imagine you are cooking dinner and you send your kid out to get a bag of flour. You say "Kid, go get me some flour. Press the buzzer on the apartment door when you're back and I'll buzz you in."
You then turn back to cooking the other parts of dinner, but you don't expect that the instant you tell your kid to go get flour, you'll look down on the counter and the flour will be there. You know that you have to wait until the buzzer buzzes, and then your kid will be back with the flour.
Submitting a network task is like that. The call to task.resume()
is similar to your request for flour. You submit the request, but you go on with what you are doing before the request has finished processing. The completion handler running is the buzzer that lets you know your kid is back with the flour.
I've created a project on Github that demonstrates using a completion handler in URLSession. The project is called Async_demo (link)
The key part is the method downloadFileAtURL()
in DownloadManager.swift
Note how the function takes completion handler as a parameter. The function creates an URLSession data task with a completion handler. Inside the completion handler, for the URLSession
data task, the downloadFileAtURL()
function calls the completion handler that was passed to it. It uses DispatchQueue.main.async()
to call the completion handler on the main thread.
This demo project uses an URLSession
data task, and explicitly disables data caching, so that the image will be reloaded each time you click the download button.
Here is the code for the downloadFileAtURL()
function in DownloadManager.swift:
func downloadFileAtURL(_ url: URL, completion: @escaping DataClosure) {
//We create a URLRequest that does not allow caching so you can see the download take place
let request = URLRequest(url: url,
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 30.0)
let dataTask = URLSession.shared.dataTask(with: request) {
data, response, error in
//Perform the completion handler on the main thread
DispatchQueue.main.async {
//Call the copmletion handler that was passed to us
completion(data, error)
}
}
dataTask.resume()
//When we get here the data task will NOT have completed yet!
}
The view controller has an IBAction function called handledDownloadButton()
. That function:
Upvotes: 3