handling location permissions instantaneously in swift

user2363025 picture user2363025 · Jun 19, 2015 · Viewed 19.3k times · Source

I am trying to implement a basic map view and add a user's current location to the map as an annotation. I have added the requestwheninuse key to my info.plist and imported coreLocation.

In my view controller's did load method, I have the following:

locManager.requestWhenInUseAuthorization()
var currentLocation : CLLocation

if(CLLocationManager.authorizationStatus() == CLAuthorizationStatus.AuthorizedWhenInUse){

    currentLocation = locManager.location
    println("currentLocation is \(currentLocation)")      
}
else{
    println("not getting location")
    // a default pin
}

I am getting the prompt re. permission to retrieve location. As this is happening I am getting my print saying not getting location, obviously because this runs before the user gets a chance to tap OK. If I elave the app and come back in I can retrieve the location and add it to the map. However, I want when the user taps OK the first time to be able to then grab the current location and add it to the map there and then. How can I achieve this? I have the following method for adding a pin:

func addPin(location2D: CLLocationCoordinate2D){
    self.mapView.delegate = self
    var newPoint = MKPointAnnotation()
    newPoint.coordinate = location2D
    self.mapView.addAnnotation(newPoint)
}

Answer

The Tom picture The Tom · Jun 19, 2015

In order to do that, you need to implement the methoddidChangeAuthorizationStatus for your location manager delegate which is called shortly after CLLocationManager is initialized.

First, at the top of the file don't forget to add : import CoreLocation

To do that, in your class where you are using the location, add the delegate protocol. Then in the viewDidLoad method (or applicationDidFinishLaunching if you are in the AppDelegate) initialize your location manager and set its delegate property to self:

class myCoolClass: CLLocationManagerDelegate {
    var locManager: CLLocationManager!

    override func viewDidLoad() {
        locManager = CLLocationManager()
        locManager.delegate = self
    }
 }

Finally, implement the locationManager(_ didChangeAuthorizationStatus _) method in the body of your class that you declared previously, this method will be called when the status of the authorization is changed, so as soon as your user clicked the button. You can implement it like this:

private func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    switch status {
    case .notDetermined:
        // If status has not yet been determied, ask for authorization
        manager.requestWhenInUseAuthorization()
        break
    case .authorizedWhenInUse:
        // If authorized when in use
        manager.startUpdatingLocation()
        break
    case .authorizedAlways:
        // If always authorized
        manager.startUpdatingLocation()
        break
    case .restricted:
        // If restricted by e.g. parental controls. User can't enable Location Services
        break
    case .denied:
        // If user denied your app access to Location Services, but can grant access from Settings.app
        break
    default:
        break
    }
}

Swift 4 - New enum syntax

For Swift 4, just switch the first letter of each enum case to lowercase (.notDetermined, .authorizedWhenInUse, .authorizedAlways, .restricted and .denied)

That way you can handle each and every case, wether the user just gave its permission or revoked it.