1

How to await result from async method?

    let url = URL(string: "https://www.cbr-xml-daily.ru/daily_json.js")
    var dict = NSDictionary()

    let task = URLSession.shared.dataTask(with: url!) { data, response, error in
        guard error == nil else {
            print(error!)
            return
        }
        guard let data = data else {
            print("Data is empty")
            return
        }

        dict = try! JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
        print("first print\(dict)")
    }

   task.resume()
   print("second \(dict)")

In first print data not empty but in second print is empty

sanmai
  • 29,083
  • 12
  • 64
  • 76
  • 3
    You don't wait; you do whatever you need to do with the result in the closure – Paulw11 Jun 16 '17 at 00:12
  • As an example, there is a model that I create based on the data received from the site. I initialize them in the closure. But then I need these data right away. The task has not yet been completed, but I'm already taking my class with empty information. How to solve this problem? –  Jun 16 '17 at 01:22
  • You either pass another closure to this function and then invoke this closure from the closure you have or just do the work in the closure. – Paulw11 Jun 16 '17 at 01:34
  • This question seems to be more about any async task (location, audio play, etc.), not just networking. – hotpaw2 Jun 28 '17 at 02:41

3 Answers3

0

That's why you need completion closure.

    func load(completion: @escaping (NSDictionary) -> ()) {
        let url = URL(string: "https://www.cbr-xml-daily.ru/daily_json.js")
        var dict = NSDictionary()

        let task = URLSession.shared.dataTask(with: url!) { data, response, error in
            guard error == nil else {
                print(error!)
                return
            }
            guard let data = data else {
                print("Data is empty")
                return
            }

            dict = try! JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary
            print("first print\(dict)")

            // Here is when you finish loading data
            completion(dict)
        }

        task.resume()
        print("second \(dict)")
    } 

EDIT

load(completion: {
    print("My data: \($0)" )
})
sanmai
  • 29,083
  • 12
  • 64
  • 76
Lawliet
  • 3,438
  • 2
  • 17
  • 28
  • But load({data in dict = data }) print(dict) dict is empty but in closure data not empty –  Jun 16 '17 at 00:20
  • I can see the data. Check my edited answer. Why don't you have `completion` anyway? – Lawliet Jun 16 '17 at 00:35
  • If you create dict in global scope and do newDict = data in closure and after closure do print then first print will with data and second without. –  Jun 16 '17 at 01:08
  • Yeah. In that case you need an HUD running while waiting for the data to be fully loaded in background. Check out https://github.com/jdg/MBProgressHUD – Lawliet Jun 16 '17 at 01:30
0

The code does not execute in the order you suspect.

Your task.resume() statement starts the task, but the task might not actually run or finish till tomorrow. Meanwhile, print("second (dict)") executes right away, today, right after the task.resume() statement, perhaps running any of before, during, or after the task completion.

The way to have code await the end of your task is to put that code that you want to await inside the task response block, where your print("first print(dict)") statement is already located. Or you can put the waiting code in another block, method, or function, called from inside that task response.

Any code after the task.resume() statement may execute way before the task even really starts.

hotpaw2
  • 70,107
  • 14
  • 90
  • 153
  • i do init my class in this block but after i need my class with data but this task not execute instantly and my class is empty. How resolve this problem? –  Jun 16 '17 at 01:15
  • In general, network tasks are never instant (they can take minutes on a slow network), and thus should never be expected to complete inside initializers, or let them lock up the UI by waiting. Whatever needs to use this class data has to be in code that is called or signalled by the task completion block. – hotpaw2 Jun 16 '17 at 01:35
0

Assume you download a URL, but just before you do that the server has crashed and was just re-booting, and it takes 50 seconds for your answer to apply. Your code should handle that and can do it easily.

You have data that is missing. Your code displaying data just has to handle that. You say you need the data - no, you don't. You just have written your code in a way that assumes it's there, so change that assumption and make it work when the data isn't there. And when the data arrives later, you update the display and show the correct data.

gnasher729
  • 51,477
  • 5
  • 75
  • 98