title: How to call function after async requests finish in SwiftUI?
body: <p>I have a function that calls 2 types of api requests to get a bunch of data i need in my app. In the function I make a request for locations, then for each location in the response I make a different request to get details of that specific location. (ex. if request 1 returns 20 locations, my second request is called 20 times, once for each location)</p>
  
  <p>My function code here:</p>
  
  <pre><code>    func requestAndCombineGData(location: CLLocation, radius: Int) {
      // Clears map of markers
          self.mapView.clear()
  
  
          // Calls 'Nearby Search' request 
          googleClient.getGooglePlacesData(location: location, withinMeters: radius) { (response) in
              print("Made Nearby Search request. Returned response here:", response)
  
              // loops through each result from the above Nearby Request response
              for location in response.results {
  
                  // Calls 'Place Details' request
                  self.googleClient.getGooglePlacesDetailsData(place_id: location.place_id) { (detailsResponse) in
                      print("GMV returned - detailsResponse.result - ", detailsResponse.result)
  
                  }
  
              }            
          }
  
      }
  </code></pre>
  
  <p>Request functions I reference above are here:</p>
  
  <pre><code>    func getGooglePlacesData(location: CLLocation, withinMeters radius: Int, using completionHandler: @escaping (GooglePlacesResponse) -&gt; ())  {
  
          for category in categoriesArray {
  
              let url = googlePlacesNearbyDataURL(forKey: googlePlacesKey, location: location, radius: radius, type: category)
  
              let task = session.dataTask(with: url) { (responseData, _, error) in
                  if let error = error {
                      print(error.localizedDescription)
                      return
                  }
  
                  guard let data = responseData, let response = try? JSONDecoder().decode(GooglePlacesResponse.self, from: data) else {
                      print("Could not decode JSON response")
                      completionHandler(GooglePlacesResponse(results:[]))
                      return
                  }
  
  
                  if response.results.isEmpty {
                      print("GC - response returned empty", response)
                  } else {
                      print("GC - response contained content", response)
                      completionHandler(response)
                  }
  
  
              }
              task.resume()
          }
  
      }
  
  
  
  
      func getGooglePlacesDetailsData(place_id: String, using completionHandler: @escaping (GooglePlacesDetailsResponse) -&gt; ())  {
  
          let url = googlePlacesDetailsURL(forKey: googlePlacesKey, place_ID: place_id)
  
          let task = session.dataTask(with: url) { (responseData, _, error) in
              if let error = error {
                  print(error.localizedDescription)
                  return
              }
  
              guard let data = responseData, let detailsResponse = try? JSONDecoder().decode(GooglePlacesDetailsResponse.self, from: data) else {
                  print("Could not decode JSON response. responseData was: ", responseData)
                  return
              }
              print("GPD response - detailsResponse.result: ", detailsResponse.result)
  
  
              completionHandler(detailsResponse)
  
  
          }
          task.resume()
  
      }
  </code></pre>
  
  <p>After I get all the data im requesting (or even as the data is coming in) I would like to append it to an @EnvironmentObject (array) I have set up in my SceneDelegate.swift file. Im using the data in multiple places in my app so the @EnvironmentObject serves as a 'source of truth'.</p>
  
  <p>I tried accomplishing this using the code below, but keep getting the error  -  <em>"Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates."</em></p>
  
  <pre><code>    func requestAndCombineGData(location: CLLocation, radius: Int) {
      // Clears map of markers
          self.mapView.clear()
  
  
          // Calls 'Nearby Search' request 
          googleClient.getGooglePlacesData(location: location, withinMeters: radius) { (response) in
              print("Made Nearby Search request. Returned response here:", response)
  
              // loops through each result from the above Nearby Request response
              for location in response.results {
  
                  // Calls 'Place Details' request
                  self.googleClient.getGooglePlacesDetailsData(place_id: location.place_id) { (detailsResponse) in
                      print("GMV returned - detailsResponse.result - ", detailsResponse.result)
  
  
                  // THIS IS WHERE I TRY TO UPDATE MY @ENVIROMETOBJECT
                  self.venueData.venuesdataarray.append(detailsRespose.result)
  
  
                  }
  
              }            
          }
  
      }
  </code></pre>
  
  <p>I believe I need to make sure the requests complete THEN try to update my @EnvironmentObject, but I do not know how to do that.</p>
  
  <hr>
  
  <p>EDIT - providing my VenueData struct as requested in comments:</p>
  
  <pre><code>struct VenueData : Identifiable {
  
      var id = UUID()
      var name : String
      var geometry : Location?
      var rating : String?
      var price_level : String?
      var types : [String]?
      var formatted_address : String?
      var formatted_phone_number : String?
      var website : String?
      var photo_reference : String?
  
  
      enum CodingKeysDetails : String, CodingKey {
          case geometry = "geometry"
          case name = "name"
          case rating = "rating"
          case price_level = "price_level"
          case types = "types"
          case opening_hours = "opening_hours"
          case formatted_address = "formatted_address"
          case formatted_phone_number = "formatted_phone_number"
          case website = "website"
      }
  
  
      // Location struct
      struct Location : Codable {
  
          var location : LatLong
  
          enum CodingKeys : String, CodingKey {
              case location = "location"
          }
  
  
          // LatLong struct
          struct LatLong : Codable {
  
              var latitude : Double
              var longitude : Double
  
              enum CodingKeys : String, CodingKey {
                  case latitude = "lat"
                  case longitude = "lng"
              }
          }
      }
  
  
  
  }
  
  
  class VenueDataArray: ObservableObject {
      @Published var venuesdataarray : [VenueData] = [
          VenueData(name: "test_name")
      ]
  }
  </code></pre>
  
  <hr>
  
  <p><strong>Solution Edit - I tried using this snippet of code within my second api request and it solved my issue, although i do not understand why I need to do this</strong></p>
  
  <pre><code>    DispatchQueue.main.async {
          self.venueData.venuesdataarray.append(RESPONSE_DETAILS_HERE)
      }
  </code></pre>
  
  <p><em>Originally I had asked, Does anyone know how I can update my @EnvironmentObject after all the requests complete?</em></p>
  
  <p><strong>Does anyone know why the snippet I have above makes everything work?</strong></p>
  
  <p>Id just like to understand what im doing and maybe someone could learn something if they find this</p>