How to Access values Outside Task.Resume()?

I'm attempting to store the values that I've obtained from a google API and then use them once the task.resume() has finished. But currently, the arrays are always empty once I've left the task. Is there anyway to go about this? Thank you.

func nearBy() -> ([String], [String], [CLLocationCoordinate2D], Int)
  {
    var counter = 0
    var arraylength = 0

    var googleURLAPI = URLComponents(string: "https://maps.googleapis.com/maps/api/place/nearbysearch/json")!

    googleURLAPI.queryItems = [
      URLQueryItem(name: "location", value: "\(origin.latitude),\(origin.longitude)"),
      URLQueryItem(name: "radius", value: range),
      URLQueryItem(name: "type", value: "restaurant"),
      //URLQueryItem(name: "price_level", value: price),
      URLQueryItem(name: "keyword", value: keyword),
      URLQueryItem(name: "key", value: KEY),
      //URLQueryItem(name: "pagetoken", value: pagetoken)
      ]
    print(googleURLAPI.url!)
         
    var urlRequest = URLRequest(url: googleURLAPI.url!)
              
    urlRequest.httpMethod = "GET"
         
    let task = URLSession.shared.dataTask(with: urlRequest) { [self]
          (data, response, error) in
          do {

            if let error = error {
              throw error
            }
            guard let data = data else {
              print("data is nil")
              return // or throw some error
            }
            
            guard let jsonDict = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
                print("response data is not a JSON object")
                return // or throw some error
              }

            guard let status = jsonDict["status"] as? String, status == "OK" else {
                print("API error, status is not OK")
                return // or throw some error
              }
                          
            guard let results = jsonDict["results"] as? [[String: Any]] else {
                print("`results` is not an Array of JSON object")
                return // or throw some error
              }
             
            for result in results {
              guard (result["name"] as? String) != nil else {
                print("value for `name` not found or not object")
                return // or throw some error or ignore and continue
              }
              arraylength = arraylength + 1
            }

            for result in results{
                guard let name = result["name"] as? String else {
                  print("value for `name` not found or not object")
                  return // or throw some error or ignore and continue
                }
                 
                guard let geometry = result["geometry"] as? [String: Any] else {
                  print("value for `geometry` not found or not object")
                  return // or throw some error or ignore and continue
                }
                guard let location = geometry["location"] as? [String: Double] else {
                  print("value for `location` not found or not object")
                  return // or throw some error or ignore and continue
                }
                guard let lat = location["lat"],
                  let lng = location["lng"] else {
                  print("value for `lat` or `lng` not found or not number")
                  return // or throw some error or ignore and continue
                }
                 
                guard let place = result["place_id"] as? String else {
                  print("value for place not found or not number")
                  return // or throw some error or ignore and continue
                }             
                 
                DispatchQueue.main.async
                {

                  self.nameArray.append(name)
                  let coord = CLLocationCoordinate2D(latitude: lat, longitude: lng)
                  self.locationArray.append(coord)
                  self.placeArray.append(place)
                   
                  counter += 1
                 if counter == arraylength
                  {
                    self.random(counter: counter)
                  }
                }

              }
              } catch {
                print(error)
                let title = NSLocalizedString("There was an Error", comment: "")
                let message = NSLocalizedString("We encountered an error while trying to connect to Google. Try again later.", comment: "")
                let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "Okay!", style: .default))
                self.present(alert, animated: true, completion: nil)
              }
        }
    task.resume()
    return ( nameArray, placeArray, locationArray, arraylength)
  }
Answered by Claude31 in 696020022

First you need to understand what's happening.

When you call

    let task = URLSession.shared.dataTask(with: urlRequest) { [self]

you "send" the closure to another thread. And task.resume() will then order executing.

But, without waiting (that's the purpose), code in the func has continued and return is executed before task was executed.

You have several ways to solve:

  • You assign the result of the return to some var in your class
  • let's assume those var are:
var theNameArray,
var thePlaceArray, 
var theLocationArray, 
var theArraylength  // I do not see the need for such a thing

Then, remove the return values from the func,

func nearBy() {

and assign them to the class properties after the catch

theNameArray = nameArray
// idem for others
  • you could use semaphores

  • you could also use await/async which is made for this. But that requires some code refactoring

Accepted Answer

First you need to understand what's happening.

When you call

    let task = URLSession.shared.dataTask(with: urlRequest) { [self]

you "send" the closure to another thread. And task.resume() will then order executing.

But, without waiting (that's the purpose), code in the func has continued and return is executed before task was executed.

You have several ways to solve:

  • You assign the result of the return to some var in your class
  • let's assume those var are:
var theNameArray,
var thePlaceArray, 
var theLocationArray, 
var theArraylength  // I do not see the need for such a thing

Then, remove the return values from the func,

func nearBy() {

and assign them to the class properties after the catch

theNameArray = nameArray
// idem for others
  • you could use semaphores

  • you could also use await/async which is made for this. But that requires some code refactoring

How to Access values Outside Task.Resume()?
 
 
Q