user1949387
user1949387

Reputation: 1275

IOS Swift value of an Integer is not being saved

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

Answers (1)

Duncan C
Duncan C

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.

EDIT:

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:

  • Disables the download button and displays an activity indicator view.
  • Invokes the download manager's downloadFileAtURL() to download a file, passing in a completion handler. The completion handler does a fair amount of error checking. If all is well, it converts the resulting data to a UIImage and installs it in an image view, and then calls a method that does some animation on the image, then discards the image and re-enables the download button.

Upvotes: 3

Related Questions