iOS8 CoreLocation Permissions

Have you been stung by Core Location when you ported your iPhone app to iOS8? Here was my experience: All calls to locationManager:didUpdateLocations: stopped occurring (eg no more location events came in), and the prompt to the user asking for permission for location access stopped popping up. Something was definitely up. Hopefully I can help you here.

TL;DR -> Jump to the 'How to update your app' subheading below.

Unfortunately, there's no mention of any changes to CoreLocation in the Xcode6 release notes. Indeed, at the time of writing, even apple's class reference does not describe what you need to be doing yet: CLLocationManager.

This is a real shame, because they've overhauled the permissions system, rendering your old apps lifeless. I presume any apps compiled against Xcode 5's iOS7 SDK will continue to work in some kind of 'compatability mode', however once you start compiling in Xcode6, you'll need to deal with this.

New permissions system

It appears that they're basically fed up with people bringing their iPhones into Genius bars, complaining about their battery life, and finding some lousy app that's leaving the GPS turned on while it's in the background.

So they've separated location permissions into two parts: the ability to get locations while the app is activated, and the ability to get location updates while your app is in the background. I guess Apple figures this will make it more obvious to users when an app is going to kill their battery in the background, and save a few trips to the Genius bar.

So now one of the authorisation status enums is deprecated, and two more are in its place:

Deprecated, for iOS7 and below:

For iOS8+
kCLAuthorizationStatusAuthorizedAlways <- For background battery-killers.
kCLAuthorizationStatusAuthorizedWhenInUse <- For sensible apps.

Also, there are now methods you need to call to request permission, instead of the permission prompt automatically appearing as it did in iOS7 and below:

requestAlwaysAuthorization <- 'May I please kill your battery?'
requestWhenInUseAuthorization <- For sensible apps.

How to update your app

So, here's what you need to do. I'm assuming below that you only want foreground access.

Firstly, wherever your app checks the authorizationStatus value, you'll need to compare against the two new enums. To keep your app working across iOS7 and 8, I recommend that before you create a CLLocationManager instance, you should check that authorizationStatus is neither kCLAuthorizationStatusRestricted nor kCLAuthorizationStatusDenied, and disregard the authorised states. If you really need to directly check the authorised states, keep in mind that iOS7's kCLAuthorizationStatusAuthorized == iOS8's `kCLAuthorizationStatusAuthorizedAlways'.

Next, after you instantiate your CLLocationManager, you'll need to call requestWhenInUseAuthorization (or requestAlwaysAuthorization) so that iOS can prompt the user for access. To keep your app compatible with iOS7, do a responds check first. See below:

if ([myLocationManager respondsToSelector:
    @selector(requestWhenInUseAuthorization)]) {
    [myLocationManager requestWhenInUseAuthorization];

You can call the above every time you create a location manager, if you've already got permission it won't prompt again, making your code simpler.

Lastly, you need to update your app's info.plist file to have the correct permission prompt. You'll need two keys, the first for iOS7 and the next for iOS8. See below:

<string>My App would pretty please like to use your GPS.</string>
<string>My App would pretty please like to use your GPS.</string>

Now that step above is dastardly, as that key is undocumented (as far as I can tell) in the CoreLocation reference. If anyone from Cupertino sees this, please nudge your tech writers!

Anyway, hope this gets you un-stuck and enjoying iOS8/Xcode 6. Good luck if you're trying Swift, however I'll give it until at least Xcode 6.1 before I trust it.

Thanks for reading! And if you want to get in touch, I'd love to hear from you: chris.hulbert at gmail.