2

I am trying to get some data from a local server, using a piece of code which worked in an Xcode playground file:

       URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) -> Void in


            if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
                friend_ids = (jsonObj!.value(forKey: "friends") as? NSArray)!
            }

        }).resume()

return friend_ids

Having read some similar questions on this topic, I am aware that the URLSession runs asynchronously, such that the function is returning a nil value before any data is obtained from the server. I also think I understood that a completion handler can be used to ensure that the data is actually obtained before moving on, but unfortunately I wasn't really able to understand how to implement one. Might someone be able to show me how a completion handler would be used in this simple example, to ensure that the is obtained from the server before the variable is returned?

Thank you!

jasperthedog
  • 129
  • 3
  • 9

1 Answers1

6

If you have a function that is itself doing asynchronous work, it cannot have a return value that represents the result of that asynchronous work (because function returns are immediate). Therefore, functions that do asynchronous work must take a closure as a parameter which accepts the expected result and is called when the asynchronous work is completed. So, in the case of your code:

func getFriendIds(completion: @escaping (NSArray) -> ()) {
    URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) -> Void in
        if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
            friend_ids = (jsonObj!.value(forKey: "friends") as? NSArray)!
            completion(friend_ids) // Here's where we call the completion handler with the result once we have it
        }
    }).resume()
}

//USAGE:

getFriendIds(completion:{
    array in
    print(array) // Or do something else with the result
})
Daniel Hall
  • 13,457
  • 4
  • 41
  • 37
  • 1
    I've tried this, but it appears to ignore the completion block. If I step through line by line, it just goes from the URLSession line to the resume line twice, and then exits the function? – jasperthedog Dec 29 '17 at 11:47
  • 1
    What URL are you trying to retrieve? The request could be either timing out or failing. Particularly if the URL is http:// and not https://, in which case it will be blocked by iOS App Transport Security unless you create an exception. Any information printing to the console? – Daniel Hall Dec 29 '17 at 13:51