4

How to request a user's location

 3 years ago
source link: https://sarunw.com/posts/how-to-request-user-location/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to request a user's location

08 Jun 2020 ⋅ 5 min read ⋅ CoreLocation

Table of Contents

Getting a user's location is a common task that many apps do, but in the distant past (iOS 8), the process isn't as simple as it sounds. If you are still using startUpdatingLocation as a way to get a user's location, you need to refresh your knowledge right now. The method we are going to talk about is requestLocation() which requests the one-time delivery of the user's current location.

Add privacy description

Since we try to access privacy-sensitive data (location), Apple required us to provide a usage description for that data. This would be shown in the permission alert when requesting to access user's location.

Add Privacy - Location When In Use Usage Description key (NSLocationWhenInUseUsageDescription) into Info.plist file.

sponsor-codeshot.png

Desired Accuracy

After setting the usage description, we can go ahead and start coding. First, initialize CLLocationManager and set the desired accuracy. The default value is to the most accurate one. You can change this if you want less accurate value.

For iOS and macOS, the default value of this property is kCLLocationAccuracyBest. For watchOS, the default value is kCLLocationAccuracyHundredMeters.

The other accuracies are as follows.

public let kCLLocationAccuracyBestForNavigation: CLLocationAccuracy
public let kCLLocationAccuracyBest: CLLocationAccuracy
public let kCLLocationAccuracyNearestTenMeters: CLLocationAccuracy
public let kCLLocationAccuracyHundredMeters: CLLocationAccuracy
public let kCLLocationAccuracyKilometer: CLLocationAccuracy
public let kCLLocationAccuracyThreeKilometers: CLLocationAccuracy
import UIKit
import CoreLocation

class ViewController: UIViewController {

let locationManager = CLLocationManager()

override func viewDidLoad() {
super.viewDidLoad()

locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
}

Request location authorization

Before we can use any location service, we need to ask for user authorization. To do that, we simply assign our view controller (or any object) as a CLLocationManager's delegate and listen for locationManager(_:didChangeAuthorization:) delegate callback.

class ViewController: UIViewController, CLLocationManagerDelegate {

let locationManager = CLLocationManager()

override func viewDidLoad() {
super.viewDidLoad()

locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
}

// MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
print("notDetermined")
manager.requestWhenInUseAuthorization()
default:
break
}
}
}

CLLocationManager is guaranteed to call the delegate method with the app's initial authorization state and all authorization state changes. If users haven't grant or deny the permission, we will get .notDetermined status, which is where we will request for location permission.

Request user's location

After call manager.requestWhenInUseAuthorization(), user will prompt with permission alert asking to authorize location permission.

Location permission authorization alertLocation permission authorization alert

Tap Allow While Using App or Allow Once would change the status to .authorizedWhenInUse. That's where we can request for user location.

 // MARK: - CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

switch status {
case .notDetermined:
print("notDetermined")
manager.requestWhenInUseAuthorization()
case .restricted:
print("restricted")
// Inform user about the restriction
break
case .denied:
print("deined")
// The user denied the use of location services for the app or they are disabled globally in Settings.
// Direct them to re-enable this.
break
case .authorizedAlways, .authorizedWhenInUse:
print("authorized")
manager.requestLocation()
}
}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Success \(locations.first)")
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Failed \(error)")
}

CLLocationManager will start getting user's location once we call manager.requestLocation(). The result (whether success or fail) will be return in the same delegate methods as calling startUpdatingLocation(), locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) and locationManager(_ manager: CLLocationManager, didFailWithError error: Error).

After getting the result, the location services are automatically stopped.

From my test, there is a chance that you will receive duplicate call of success callback locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]). This quite destroy the purpose of requestLocation(), the good thing is the locations received seem to be valid (not the stale one).

So beware of this bug and adjust your code accordingly.

sponsor-codeshot.png

Conclusion

If you have ever try to request a user's location in the past, you know that the process isn't as straight forward as this. We have to rely on startUpdatingLocation(), then manually stopUpdatingLocation() after we filter out stale locations and get a valid one.

Related Resources


Get new posts weekly

If you enjoy this article, you can subscribe to the weekly newsletter.

Every Friday, you’ll get a quick recap of all articles and tips posted on this site — entirely for free.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron

Tweet

Share

Previous
What is backIndicatorTransitionMaskImage

To set a custom image for the back button, we need to set an image to both backIndicatorImage and backIndicatorTransitionMaskImage, but what does backIndicatorTransitionMaskImage really mean?

Next
Animation delay and repeatForever in SwiftUI

Explore how delay and repeatForever affect an animation.

← Home


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK