Reputation: 269
I'm still building my skills in SwiftUI, so thank you to any experienced developers that can help answer my question.
For context, I am building an app that displays markers on a map, and also a list of the markers as well. Im using an @EnvironmentObject to hold the data so that there is ‘one source of truth’ and so that the map and list automatically update when the data changes.
To get the data I need I make 2 types of api requests. One to get a list of locations, then for each location I make a request for extra information about each respective location.
In order to update the @EnvironmentObject I have setup though, I need to complete all requests then update it from the main thread of the application. So my issue is that I need to be able to call a function only after the api requests finish.
The code below shows how I implement api requests in my app at the moment, but Im hoping someone can show me how to change them so that I can call a function after the 2 requests complete:
Here is a skeleton of my file that displays a map & where I make my api requests: The main part is in the ‘extension GMController’ at the bottom
import SwiftUI
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
import Foundation
struct GoogMapView: View {
var body: some View {
GoogMapControllerRepresentable()
}
}
class GoogMapController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
var mapView: GMSMapView!
let defaultLocation = CLLocation(latitude: 42.361145, longitude: -71.057083)
var zoomLevel: Float = 15.0
let marker : GMSMarker = GMSMarker()
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.distanceFilter = 50
locationManager.startUpdatingLocation()
locationManager.delegate = self
let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude, longitude: defaultLocation.coordinate.longitude, zoom: zoomLevel)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setMinZoom(14, maxZoom: 20)
mapView.settings.compassButton = true
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.settings.scrollGestures = true
mapView.settings.zoomGestures = true
mapView.settings.rotateGestures = true
mapView.settings.tiltGestures = true
mapView.isIndoorEnabled = false
marker.position = CLLocationCoordinate2D(latitude: 42.361145, longitude: -71.057083)
marker.title = "Boston"
marker.snippet = "USA"
marker.map = mapView
view.addSubview(mapView)
}
// Handle incoming location events.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location: CLLocation = locations.last!
print("Location: \(location)")
let camera = GMSCameraPosition.camera(withLatitude: location.coordinate.latitude, longitude: location.coordinate.longitude, zoom: zoomLevel)
mapView.animate(to: camera)
}
// Handle authorization for the location manager.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
// Display the map using the default location.
mapView.isHidden = false
case .notDetermined:
print("Location status not determined.")
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("Location status is OK.")
}
}
// Handle location manager errors.
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
print("Error: \(error)")
}
}
// UIViewControllerExtension
extension GMController {
func requestAndCombineGData(location: CLLocation, radius: Int) {
// Clears map of markers
self.mapView.clear()
// vvv THIS IS WHERE I AM TRYING TO CALL THE REQUESTS IN SUCCESSION AND THEN CALL A FUNCTION AFTER THE REQUESTS ARE FINISHED vvv
// 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' to get the 'place_id' and make 'Place Details'
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)
}
}
}
}
}
struct GoogMapControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: UIViewControllerRepresentableContext<GMControllerRepresentable>) -> GMController {
return GMController()
}
func updateUIViewController(_ uiViewController: GMController, context: UIViewControllerRepresentableContext<GMControllerRepresentable>) {
}
}
Does anyone know how I can alter my code so that I am able to call a function only after all the api requests complete that is able to use the data from the requests?
Upvotes: 0
Views: 804
Reputation: 663
Assuming that you are calling your second API call from the first or when first API call ends.
Add an observer on your viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(
self.methodOfReceivedNotification(notification:)), name: Notification.Name
("NotificationIdentifier"), object: nil)
then add post notification on the 'location' api call when a case is met or when a condition is satisfied or when call is finished
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"),
object: nil)
Then inside this, write whatever you want to happen after the location call is finished.
@objc func methodOfReceivedNotification(notification: Notification)
{
///
}
Upvotes: 1