I have a view model called MapViewModel where one of the properties gets assigned the CLLocationCoordinate2D of the user if location services is enabled. However, it takes time to get that value from the location manager, and I am using that CLLocationCoordinate2D value in another view model. Right now I am assigning a new variable in my second view model with the user's coordinates like this: self.center = mapViewModel.locationManager!.location!.coordinate
However, that line is running before there is a value in the location manager, and obviously I get an error saying that nil was found. How do I make the program wait for location manager to have a value before assigning the user's location to self.center in my second view model? I feel like this is where async code comes in but I am not exactly sure how to wait for location Manager to have a value before trying to access that value in location manager.
CodePudding user response:
I'm guessing you are using CCLocationManager. And to get live updates on location all you need to do is to register to its delegate. Then you will be able to get updates using one of its update functions. Once you get an update you can assign the new location and continue with you code.
I highly recommend on first reading the documentation around the API that Apple provides.
CodePudding user response:
As pointed out by CloudBalancing, the CLLocationManager allows you to register a delegate. Once you do so, you may set whichever ViewModel to be the delegate and specify the code you wish to run within the didUpdateLocations
method.
private let locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// do something
}
You are correct that location will not be obtained immediately, I recently solved a similar issue by initially providing a default value for user location (coordinates of zero, zero) and in my viewDidLoad
method I placed the following:
if lat == 0 && lon == 0 {
DispatchQueue.main.asyncAfter(deadline: .now() 2, execute: {
self.update(lat: lat, lon: lon)
})
}
In the code above, the .now() 2
will reload the ViewModel after two seconds, which seems to be sufficient time to obtain location in the context of my app.
I am not saying this is a best practice, but it served as a work around for my situation.
CodePudding user response:
You are thinking about this wrong.
You don't "...Wait for location manager to have a value". You set your app up to be the location manager's delegate, ask for location updates, and then when your delegate methods get called, you use them to update the map.
Think of it like a person waiting for a phone call. The person doesn't stop breathing, waiting for the phone call. They go about their business, and when the phone rings, they answer it.
Your program continues to respond to user actions while it is waiting for a location update, and when it gets the update (the phone rings) it responds to that by updating the location on the map.
It sounds like you need to read up on event-driven programming and the delegate design pattern. Once you've read up on those two subjects, find a demo app that displays a map with the user's location on it and updates the map as the user moves around. Study the way it:
- Asks the user for permission to ask the system for their location.
- Creates an instance of the location manager, saves it to an instance variable, and sets itself as the delegate of that location manager.
- Asks to start location updates.
- Implements the delegate methods that respond to location updates so that they display the user's location on the map.
You'll need to decide what to display before you get the first location update. (The first location update will probably take several seconds.) You can display some fixed location like your country's capital, your home city, or whatever means something to you. You can hide the map and show a "loading location... please wait" message in that space.
You can also cache the location from the last time the user ran your app, and display that first. (UserDefaults
is good for that sort of caching. It's a good choice for saving small amounts of data like a location.)