-1

I have this code:

override func viewDidLoad() {
    super.viewDidLoad()

    navigationItem.title = "Tavolo n° " + UserDefaults.standard.string(forKey: "number")!
    self.navigationController?.isNavigationBarHidden = false
    navigationController?.navigationBar.barTintColor =  UIColor(red: 0.30, green: 0.69, blue: 0.31 , alpha: 1.0)
    navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor:UIColor.white]
    navigationController?.navigationBar.tintColor = UIColor.white;
    pickerView.delegate = self
    pickerView.dataSource = self
    name.delegate = self

    let myUrl = NSURL(string: "http://192.168.1.116/myorder/data/fetch_name.php");
    let request = NSMutableURLRequest(url: myUrl! as URL);
    request.httpMethod = "POST";
    let postString = "number=\(UserDefaults.standard.integer(forKey: "number"))";
    request.httpBody = postString.data(using: String.Encoding.utf8);
    let task = URLSession.shared.dataTask(with: request as URLRequest){
        data, response, error in
        if error != nil{
            print("error=\(String(describing: error))")
            return
        }

        var _: NSError?

        do{

            let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSArray

            if let parseJSON: NSArray = json {

                for index in 0...parseJSON.count-1 {
                    if (parseJSON[index] is NSNull){
                            let myAlert = UIAlertController(title: "Attenzione\n", message: "Nessun nominativo per questo tavolo", preferredStyle: UIAlertControllerStyle.alert);

                            let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default){ action in }

                            myAlert.addAction(okAction);
                            self.present(myAlert, animated: true, completion: nil);
                    }else{
                        let nomi = parseJSON[index] as! [String:Any]
                        ViewController.listNames.append(nomi["name"] as! String)
                    }
                }

            }
        }catch{
            print("error=\(error)")
            return
        }
    }
    task.resume();
    print(ViewController.listNames)
    selectedName = ViewController.listNames[0]

    NotificationCenter.default.addObserver(self, selector: #selector(NamesVC.keyboardWillShow(sender:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(NamesVC.keyboardWillHide(sender:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

Sometimes the response returns before printing the first element of ViewController.listNames, but sometimes not so it prints nothing. How can I wait until the response returns? I have tried other answers on stackOverflow, but I can't do it

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Marco
  • 65
  • 11
  • Rather than waiting, move the code that uses `listNames` into the completion handler so you're sure you have the data before referencing it. – Phillip Mills Oct 24 '17 at 14:16
  • You don't wait. You never do. You use a callback instead. See an example here: https://stackoverflow.com/a/31264556/2227743 – Eric Aya Oct 24 '17 at 14:18
  • 1
    Completely unrelated but the line `var _: NSError?` is hilarious (and useless) :-) – vadian Oct 24 '17 at 14:27
  • I tried using a completion handler like the one you have posted @Moritz but it didn't work. Now I'll try with the answer in your link – Marco Oct 24 '17 at 17:00
  • @vadian I know that in my code there are some errors because I'm quite new in programming in Swift – Marco Oct 24 '17 at 17:01
  • I tried your answer @Moritz but it prints that line in viewDidLoad before printing the json like in the link – Marco Oct 24 '17 at 17:47

1 Answers1

1

This happens because when you resume the task, it goes off and gets data over the internet, which might take some time. Because of that, resume finishes immediately instead of waiting until the network connection finishes. Your print is on the next line. Sometimes the network connection will be fast and it will finish before you reach the print. Other times it won't be so fast and the print happens first. It's a classic example of a race condition-- two things run separately from each other, and it's unpredictable which one will finish first.

All of this is why the task has a completion block-- which is your closure that begins with if error != nil{. Code inside that closure won't happen until the task finishes.

You should put your print statement inside that closure, so that you can be sure that the network connection has finished before it happens.

Tom Harrington
  • 69,312
  • 10
  • 146
  • 170
  • I know that is a "race condition" as you said. The print line is only an example. In my case with the names that return from PHP, I want to show a pickerView (I haven't post it in my question). Sometimes the pickerView loads with the names and sometimes not. I want that it loads always with the names that are returning, also if the connection is low and it takes more time – Marco Oct 24 '17 at 20:59
  • The same thing I said about the print statement applies-- if the code depends on the results of the call, it needs to be inside that closure. Or else, it needs to be triggered by a call from the closer (maybe a notification that the network call is completed, that causes the UI to reload). – Tom Harrington Oct 24 '17 at 21:01
  • Ok, I understand what you're saying but I can't do it. I display the pickerView with principal methods: numberOfRowsInComponent, titleForRow... – Marco Oct 24 '17 at 21:08
  • You probably need to call `reloadAllComponents()` from the closure, to tell the picker to load the new data. – Tom Harrington Oct 24 '17 at 21:09
  • I have done this and it works, but I see that the names appear one second later and I think it is not so good in an app (there is a kind of delay) – Marco Oct 24 '17 at 21:21
  • Are you sure that you're doing that on the main queue? UI updates from other queues are usually delayed. – Tom Harrington Oct 24 '17 at 21:23
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/157409/discussion-between-marco-and-tom-harrington). – Marco Oct 24 '17 at 21:26
  • Let's not. You're now asking about things that are completely unrelated to your original question. You should ask a new question for your current needs. – Tom Harrington Oct 24 '17 at 21:28
  • Ok, but with lot of tests I noticed that your method doesn't wait always the response – Marco Oct 24 '17 at 21:54