Search code examples
iosxcodeswiftnsurlrequest

Why debugger shows that variable is empty


I have implemented url request to server and going to work with received JSON. But there is strange thing I cant explain.

My ViewController class:

import UIKit

class TopRatedViewController: UIViewController {

    var importedRates: NSArray = []
    var successfullyConnected: Bool = true


    @IBAction func buttonTapped(sender: AnyObject) {
        print(importedRates)
    }


    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK: Connection to web service
        let urlString: String = "http://alma.com/get.php"

        let url = NSURL(string: urlString)
        let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session = NSURLSession(configuration: sessionConfig)
        let task = session.dataTaskWithURL(url!, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in

            if error != nil {
                self.successfullyConnected = false
            }else {
                var jsonresult = NSArray()
                do {
                    jsonresult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
                        self.importedRates = jsonresult
                } catch _ {
                    print("error loading rates")
                }
            }
        })

        task.resume()
        print(importedRates)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

So if I run my application print(importedRates) command prints empty array. And in debug window I see that there is nothing in it.

BUT. if I click on button which then is supposed to print this variable - it successfully prints an array with 40 elements. Can anybody explain why it is working like this?

I was expecting that this variable should be populated with data in viewdidload method.


Solution

  • You are starting an asynchronous call in viewDidLoad that sets self.importedRates = jsonresult on completion.

    Everything in the completionHandler is executed with a very high probability after print(importedRates), to be more precise: At the time, the network request comes back (depending on your network and the request, a few microseconds to several minutes)

    That is why the button press works: By the time you push the button, the request did finish and populate the array correctly.

    Using synchronous requests on the main thread is highly discouraged. This might lead to your app being unresponsive (because all user input in handled on the main thread and cannot proceed as long as the synchornous call is waiting for a response.

    Rather, if the data you expect arrives, call a method of the view controller from within the completionHandler that uses the now-set property in the way you want it to be used. For example, if you were trying to populate a self.tableView : UITableView!:

    let task = session.dataTaskWithURL(url!, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
    
                if error != nil {
                    self.successfullyConnected = false
                }else {
                    var jsonresult = NSArray()
                    do {
                        jsonresult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
                            dispatch_async(dispatch_get_main_queue()) {
                            self.importedRates = jsonresult
                            self.tableView.reloadData() // look here
     }
                    } catch _ {
                        print("error loading rates")
                    }
                }
            })
    
            task.resume()