In the following code, I have a LocationManager
class which provides the city name of the current location via the @Published
property wrapper lastSearchedCity
.
Then I have a SearchManagerViewModel
class that should be in charge of presenting the city name on SwiftUI
views based on some conditions (not currently shown in the code below) via the @Published
property wrapper cityName
. It properly shows the city name when I call the searchAndSetCity()
method from ContentView.swift
inside an onAppear
modifier.
My issue is that if the user turned Location Services
off and turns it back On while he/she is in the ContentView.swift
the Text
view doesn't update, which is understandable since the searchAndSetCity()
method would need to be called again.
How can I call the searchAndSetCity()
method located inside the SearchManagerViewModel
class every time the locationManagerDidChangeAuthorization(_ manager: CLLocationManager)
method is called? I believed this method is called every time the authorization status changes.
LocationManager Class
final class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { private let locationManager = CLLocationManager() @Published var lastSearchedCity = "" var hasFoundOnePlacemark:Bool = false func checkIfLocationServicesIsEnabled(){ DispatchQueue.global().async { if CLLocationManager.locationServicesEnabled(){ self.locationManager.delegate = self self.locationManager.desiredAccuracy = kCLLocationAccuracyBest/// kCLLocationAccuracyBest is the default self.checkLocationAuthorization() }else{ // show message: Services desabled! } } } private func checkLocationAuthorization(){ switch locationManager.authorizationStatus{ case .notDetermined: locationManager.requestWhenInUseAuthorization() case .restricted: // show message case .denied: // show message case .authorizedWhenInUse, .authorizedAlways: /// app is authorized locationManager.startUpdatingLocation() default: break } } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { checkLocationAuthorization() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { hasFoundOnePlacemark = false CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)-> Void in if error != nil { self.locationManager.stopUpdatingLocation() // show error message } if placemarks!.count > 0 { if !self.hasFoundOnePlacemark{ self.hasFoundOnePlacemark = true let placemark = placemarks![0] self.lastSearchedCity = placemark.locality ?? "" } self.locationManager.stopUpdatingLocation() }else{ // no places found } }) } }
SearchManagerViewModel Class
class SearchManagerViewModel: ObservableObject{ @Published var cityName = "" // use this directly in SwifUI views @ObservedObject private var locationManager = LocationManager() // Call this directly fron onAppear in SwiftUI views // This method is more complex than what is shown here. It handles other things like HTTP requests etc. func searchAndSetCity(){ locationManager.checkIfLocationServicesIsEnabled() self.cityName = locationManager.lastSearchedCity } }
ContentView.swift
struct ContentView: View { @StateObject private var searchManager = SearchManagerViewModel() var body: some View { VStack { Text(searchManager.cityName) .font(.callout) } .onAppear{ searchManager.searchAndSetCity() } } }