Having trouble with ObserveObject Array changes in my code

79 views Asked by At

Currently, my code finds nearby parks around the user's current location and outputs the name/location in the console fine (using Google Maps & Google Places API). Console Output. The next step I'm trying to complete in my app is to annotate each one of these parks on the map with a marker in the GoogleMapsView.swift file before I return the mapView. My issue is that I can't figure out how to do this after the park locations are found. So my question is how can I observe the park location changes in the array to be able to mark them on the map afterwards? Here is the code I have. In my ContentView.swift, I simply pass the GoogleMapsView into view.

PlacesManager.swift:

import GooglePlaces

class PlacesManager: NSObject, ObservableObject, LocationManagerDelegate {
    
    @Published var places = [Place]()
    lazy var googleClient: GoogleClientRequest = GoogleClient()
    var locationManager: LocationManager?
    var searchRadius : Int = 700
        
    override init() {
        super.init()
        load()
    }
    
    func load() {
        locationManager = LocationManager()
        locationManager?.delegate = self
    }
    
    func locationDidChange(location: CLLocation) {
        let lat = location.coordinate.latitude
        let long = location.coordinate.longitude
        let newLocation: CLLocation = CLLocation(latitude: lat, longitude: long)
        
        fetchGoogleData(forLocation: newLocation, searchRadius: 700)
    }
    
    func fetchGoogleData(forLocation: CLLocation, searchRadius: Int) {
        //guard let location = currentLocation else { return }
        googleClient.getGooglePlacesData(location: forLocation, withinMeters: searchRadius) { (response) in
            self.places = response.results

        }
    }
    
    //... print parks in console code is here
}

LocationManager.swift:

protocol LocationManagerDelegate: class {
    func locationDidChange(location: CLLocation)
}

class LocationManager: NSObject, ObservableObject {
    
    weak var delegate: LocationManagerDelegate?
    
    private let locationManager = CLLocationManager()
    
    @Published var location: CLLocation? {
        willSet { objectWillChange.send() }
    }
    
    var latitude: CLLocationDegrees {
        return location?.coordinate.latitude ?? 0
    }
    
    var longitude: CLLocationDegrees {
        return location?.coordinate.longitude ?? 0
    }
    
    override init() {
        super.init()
        
        let status = CLLocationManager.authorizationStatus()
        
        if status == .notDetermined {
            locationManager.requestWhenInUseAuthorization()
        }
        
        if CLLocationManager.locationServicesEnabled() {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
            locationManager.startUpdatingLocation()
        }
    }
}

extension LocationManager: CLLocationManagerDelegate {
    
    func locationManager(_ manager: CLLocationManager,didChangeAuthorization status: CLAuthorizationStatus) {
        guard status == .authorizedWhenInUse else {
            return
        }
        
        locationManager.requestLocation()
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.first else {
            return
        }
        
        self.location = location
        self.delegate?.locationDidChange(location: location)
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print(error)
    }
}

GoogleMapsView.swift:

struct GoogleMapsView: UIViewRepresentable {
    
    @ObservedObject var locationManager = LocationManager()
    @ObservedObject var place: PlacesManager = PlacesManager()
    
    func makeUIView(context: Self.Context) -> GMSMapView {
        let camera = GMSCameraPosition.camera(withLatitude: locationManager.latitude, longitude: locationManager.longitude, zoom: 15)
        let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
        mapView.isMyLocationEnabled = true
        mapView.settings.rotateGestures = false
        mapView.settings.tiltGestures = false
        mapView.isIndoorEnabled = false
        mapView.isTrafficEnabled = false
        mapView.isBuildingsEnabled = false
        mapView.settings.myLocationButton = true

        return mapView
    }
}
1

There are 1 answers

0
Bryce Chan On

In my project I used UIKit to handle the map and marker, the approach is quite different from what you posted. Anyway here is one of the function I used to "refresh" the map after I updated the marker from the Place API, hope it helps~

func addMarkerFrom(places: [Place]) {
    for place in places {
        if place.user_ratings_total ?? 100 > UInt(100) && place.business_status == "OPERATIONAL" {
            if place.types?.contains(where: placeTypesFilter.contains) == false {
                DispatchQueue.main.async {
                    let marker = GMSMarker()
                    let fetchedPlace = place
                    let markerIconImage = UIImage(named: "Marker_icon_blue")
                    
                    marker.position = CLLocationCoordinate2D(latitude: (fetchedPlace.geometry?.location?.latitude)!, longitude: (fetchedPlace.geometry?.location?.longitude)!)
                    marker.title = place.name
                    marker.snippet = "\(String(describing: fetchedPlace.user_ratings_total)), \(String(describing: fetchedPlace.rating))"
                    marker.appearAnimation = .pop
                    marker.icon = self.drawMarkerWith(image: markerIconImage!, rating: fetchedPlace.rating!, userRatingTool: fetchedPlace.user_ratings_total!)
                    marker.infoWindowAnchor = CGPoint(x: 0.5, y: 2.3)
                    marker.tracksInfoWindowChanges = true
                    marker.map = self.mapView
                    filteredPlaces.append(fetchedPlace)
                }
            }
       }
    }
}