Once a button is pressed, I have a CLLocationManager that requests authorization for location services and if the user accepts, the locationManager will call startUpdatingLocation(). Once there is an updated location (which is immediately), I expect the ClLocationManagerDelegate to call didUpdateLocations and from there I immediately call manager.stopUpdatingLocation() so that I ONLY get 1 set of coordinates for the time being. However, sometimes (inconsistently) I will get two sets of coordinates as if the button was pressed twice in succession. I understand startUpdatingLocation can be tricky because it can very rapidly update your location until it is stopped but I can't seem to pinpoint where and how to avoid this! I found many threads on this same issue but nothing that has worked for my specific case.
I looked online and found this and tried a couple of the things in that thread but still could not fix it.
Below is my code:
getUserLocation() is the first function called when the button in my app is pressed.
func getUserLocation() {
self.places.removeAll()
self.placesTableView.reloadData()
LocationService.shared.requestPermissionToAccessLocation()
LocationService.shared.locationUpdated = { [weak self] location in
self?.fetchPlaces(location: location)
}
self.view.addSubview(placesTableView)
placesTableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(
[
placesTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 200),
placesTableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 150),
placesTableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10),
placesTableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -130),
]
)
}
private func fetchPlaces(location: CLLocationCoordinate2D) {
let searchSpan = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
let searchRegion = MKCoordinateRegion(center: location, span: searchSpan)
let searchRequest = MKLocalSearch.Request()
searchRequest.region = searchRegion
searchRequest.resultTypes = .pointOfInterest
searchRequest.naturalLanguageQuery = "bar"
let search = MKLocalSearch(request: searchRequest)
search.start { response, error in
guard let mapItems = response?.mapItems else {
return
}
DispatchQueue.main.async { [weak self] in
for item: MKMapItem in mapItems {
let placeMark = item.placemark as CLPlacemark
let completeBusinessString = String(format: "%@\n%@ %@\n%@, %@ %@", placeMark.name!, placeMark.subThoroughfare!, placeMark.thoroughfare!, placeMark.locality!, placeMark.administrativeArea!, placeMark.postalCode!)
self?.places.append(completeBusinessString)
}
self?.placesTableView.reloadData()
}
}
}
LocationServices singleton class
class LocationService: NSObject {
static let shared = LocationService()
lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
return manager
}()
var locationUpdated: ((CLLocationCoordinate2D) -> Void)?
override private init() {
super.init()
}
func requestPermissionToAccessLocation() {
switch locationManager.authorizationStatus {
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
case .restricted:
locationManager.requestWhenInUseAuthorization()
case .denied:
break
case .authorizedAlways:
locationManager.startUpdatingLocation()
case .authorizedWhenInUse:
locationManager.startUpdatingLocation()
default:
break
}
}
}
extension LocationService: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
manager.stopUpdatingLocation()
if let location = locations.last?.coordinate {
print(location)
locationUpdated?(location)
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch manager.authorizationStatus {
case .authorizedAlways:
manager.startUpdatingLocation()
case .authorizedWhenInUse:
manager.startUpdatingLocation()
default:
break
}
}
}
If the goal is to get just one value that tells you the location, you're making the wrong call; use
requestLocation.