Array Only Shows Populated After Second Run Through

In attempts to store info from URL string by the google api page, the array only shows population after the second run through. This is what I got:

Code Block
@IBAction func searchPlace(_ sender: Any) {
    nearBy()
  }
func nearBy()
  {
        
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: "15000"),
          URLQueryItem(name: "type", value: "Fast Food"),
          URLQueryItem(name: "keyword", value: "Food"),
          URLQueryItem(name: "key", value: "Key"),
        ]
        print(googleURLAPI.url!)
         
        var urlRequest = URLRequest(url: googleURLAPI.url!)
         
        urlRequest.httpMethod = "GET"
         
        let task = URLSession.shared.dataTask(with: urlRequest) {
          (data, response, error) in
          do {
                if let error = error {
                  throw error
                }
                guard let data = data else {
                  print("data is nil")
                  return
                }
                //# For debugging, show response data as text
                //print(String(data: data, encoding: .utf8) ?? "?")
                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
                }
                //setting jsonDict to read datta from url
                //# Better check "status"
                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
                }
                //print("or here",LocArray)
                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
                  }
                  self.nameArray.append(name)
                  let coord = CLLocationCoordinate2D(latitude: lat, longitude: lng)
                  self.locationArray.append(coord)
                }
              } 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()
    printNames()
    //gotoPlaces()
  }
func printNames()
  {
    print("name array",nameArray)
    print("location array",locationArray)
  }


Have I somehow cleared the arrays without knowing?

Answered by OOPer in 659736022

When I call the function gotoPlaces() after line 81 I get a crash and error that says:
Terminating app due to uncaught exception 'GMSThreadException', reason: 'The API method must be called from the main thread'

Then you call it from the main thread:
Code Block
do {
if let error = error {
throw error
}
//...
             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
             }
DispatcheQueue.main.async {
self.nameArray.append(name)
let coord = CLLocationCoordinate2D(latitude: lat, longitude: lng)
self.locationArray.append(coord)
self.gotoPlaces()
}
} catch {
//...
}

Solve the issue as suggested in the error message.

Have I somehow cleared the arrays without knowing?

NO. You are using Arrays before they are populated.
dataTask is an asynchronous call. The completion handler (line 25 { ... line 82 }) is executed after the task is complete.

That printNames() on line 84 is executed before the task is completed.

How to fix may depend on how you want to use the Arrays.
Again, you are not showing the code using the Arrays.
The gotoplaces() function is what utilizes the arrays:

Code Block
func gotoaces()
  {
    printNames()
    let number = Int.random(in: 0..<nameArray.count)
    destination = locationArray[number]
    let marker = GMSMarker()
    marker.position = destination
    marker.title = "Desitination"
    marker.snippet = nameArray[number]
    marker.map = self.mapView
     
    self.mapView.camera = GMSCameraPosition(target: destination, zoom: 25)
     
    getRouteSteps(from: origin, to: destination)
     
  }


I thought that since the function was called after task.resume() meant that it finished?

The gotoplaces() function is what utilizes the arrays:

Thanks for showing the code.
Then you need to call gotoaces() or (gotoplaces() or gotoPlaces()?) inside the completion handler.

I thought that since the function was called after task.resume() meant that it finished?

It is a very common misunderstanding among who are not accustomed to async call.
task.resume() just triggers to start the task. When the task is finished, iOS tell your app that the task is completed.

Another too simplified advice:
Do not write any lines after task.resume().
When I call the function gotoPlaces() after line 81 I get a crash and error that says:
Terminating app due to uncaught exception 'GMSThreadException', reason: 'The API method must be called from the main thread'





Accepted Answer

When I call the function gotoPlaces() after line 81 I get a crash and error that says:
Terminating app due to uncaught exception 'GMSThreadException', reason: 'The API method must be called from the main thread'

Then you call it from the main thread:
Code Block
do {
if let error = error {
throw error
}
//...
             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
             }
DispatcheQueue.main.async {
self.nameArray.append(name)
let coord = CLLocationCoordinate2D(latitude: lat, longitude: lng)
self.locationArray.append(coord)
self.gotoPlaces()
}
} catch {
//...
}

Solve the issue as suggested in the error message.
Doing that promotes a new error message:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'

Never mind, I found that error.

Never mind, I found that error.

If you found something wrong and fixed it by yourself, please share your solution.
There are some readers other than you and me, and more in the future.
Sharing the solution would benefit all such readers, and me.

If you found something wrong and fixed it by yourself, please share your solution.
There are some readers other than you and me, and more in the future.
Sharing the solution would benefit all such readers, and me.

There was just an extra "e" in DispatcheQueue.main.async { . Should read DispatchQueue.main.async { .
Thank you so much for your help and clarification!

There was just an extra "e"

OK, thank you for sharing the info.

Happy to hear I could help solving your issue.
Array Only Shows Populated After Second Run Through
 
 
Q