32

I have a view controller which implements the CLLocationManagerDelegate. I create a the CLLocationManager variable:

let locationManager = CLLocationManager()

Then in the viewDidLoad, I set properties:

// Set location manager properties
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
locationManager.distanceFilter = 50

The problem comes that the function gets called even before I check the authorization status.

func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    if (status == .AuthorizedWhenInUse) {
        // User has granted autorization to location, get location
        locationManager.startUpdatingLocation()
    }
}

Can anyone inform me what could be causing this to occur?

Mike Walker
  • 2,944
  • 8
  • 30
  • 62

3 Answers3

58

- locationManager:didChangeAuthorizationStatus: is called shortly after the CLLocationManager is initialised.

You can request authorization inside the delegate method if you want:

func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
    switch status {
    case .notDetermined:
        locationManager.requestAlwaysAuthorization()
        break
    case .authorizedWhenInUse:
        locationManager.startUpdatingLocation()
        break
    case .authorizedAlways:
        locationManager.startUpdatingLocation()
        break
    case .restricted:
        // restricted by e.g. parental controls. User can't enable Location Services
        break
    case .denied:
        // user denied your app access to Location Services, but can grant access from Settings.app
        break
    default:
        break
    }
}

Be aware that you need to assign the delegate in a 'timely' matter if you want this to work.

If you would somehow delay the delegate assignment, e.g. by setting it asynchronously, you might miss the initial call to - locationManager:didChangeAuthorizationStatus:.

Cublax
  • 1,232
  • 1
  • 11
  • 20
Bart Vandendriessche
  • 1,372
  • 13
  • 14
  • 2
    Thank you very much. I did not realize it gets called after initialization. – Mike Walker May 07 '15 at 17:56
  • 1
    Thanks for the answer. The apple documentation needs to be updated with this information. – Lee Kang Dec 29 '15 at 19:38
  • It seems you're right that `didChangeAuthorizationStatus` called shortly after `CLLocationManager` is initialized, but where is this documented? – bobics Jan 12 '16 at 23:45
  • Thanks @bart-vandendriessche! It's not documented anywhere @bobics, afaik! – rmartinsjr Feb 21 '16 at 22:25
  • i knew something was wrong! thanks. this is the legit answer – Led Sep 11 '17 at 04:46
  • 13
    This seems like a bug to me, I'd hardly describe this as a `didChange` of the authorization status. Writing code that reacts to actual changes in authorization status is compromised by this behavior. – mxcl Nov 30 '17 at 00:20
  • @mxcl I agree, if we need to know the current authorization status we just ask the CLLocationManager object/class, this method seems totally redundant if it doesn't do what it says it does... – turingtested Feb 15 '18 at 10:08
  • 1
    @turingtested Unfortunately, this function can't be totally ignored, as it provides the best way of tracking changes to authorization status. If the user backgrounds the app, changes location permissions, then reopens the app, the function will work the way it's supposed to. The problem is that the function also gets called on initialization of the CLLocationManager, which really makes no sense and is unfortunate. – Dafurman May 11 '18 at 22:39
  • I initialized `CLLocationManager` in `viewDidLoad()` but the function `locationManager:didChangeAuthorizationStatus:` is never called. I also put a breakpoint inside the function but nothing happens. What should I do? – Emm Dec 17 '19 at 12:31
  • 3
    In case this also does not work for someone. In ios14 this function is not called anymore, beause it is deprecated. Use: ```func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { if #available(iOS 14.0, *) { switch manager.authorizationStatus {...} } }``` – Celina Jan 27 '21 at 14:11
7

Swift 3

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        switch status {
        case .notDetermined:
            manager.requestAlwaysAuthorization()
            break
        case .authorizedWhenInUse:
            manager.startUpdatingLocation()
            break
        case .authorizedAlways:
            manager.startUpdatingLocation()
            break
        case .restricted:
            // restricted by e.g. parental controls. User can't enable Location Services
            break
        case .denied:
            // user denied your app access to Location Services, but can grant access from Settings.app
            break
        }
    }
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
  • Why does it say "Invalid redeclaration of 'locationManager(_:didChangeAuthorization:)' " When I try to implement the above locationManager(_:didChangeAuthorization:) method in swift 4. – madu Feb 20 '18 at 15:21
3

The other answers could introduce new undesirable behaviors.

You can just add a boolean and a guard to prevent the first call, with some comments explaining the bug :

var firstTimeCalled = true

// ...    

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

    guard !firstTimeCalled else {
        firstTimeCalled = false
        return
    }

    // ... send status to listeners
}
Xys
  • 8,486
  • 2
  • 38
  • 56