3

Long story short:

I'd like to make "Location" appear in the app settings without requesting for location authorization, like the Tile app does.

Image: Tile has Location in the app settings without requesting for location authorization

Steps to reproduce:

  1. Install the Tile app.
    • There is no "Location" in the app settings.
  2. Open the app, accept Bluetooth access request (does it matter?).
    • There is still no "Location" in the app settings, but Bluetooth appears.
  3. Use the app for about 20 seconds (can't be shorter).
    • "Location" appears in the app settings - how?

This is presented on the video below:

https://media.giphy.com/media/h5dPQPbBHzEhdLTrKz/giphy.gif

How can I achieve this?


Background - why

Since iOS 13, the user cannot be asked for Always location authorization directly. When the developer requests for Always authorization, the user can select only While in Use option and the app gets Provisional Always authorization. Until the user is prompted again (iOS decides when), the user will see While in Use authorization in the app settings.

Which means:

  • Always --> CLAuthorizationStatus.authorizedAlways and Always in the app settings.

  • Provisional Always --> also CLAuthorizationStatus.authorizedAlways but While in Use in the app settings.

This is well described in this Stack Overflow answer.

The problem is that the application cannot read the location in the background without Always authorization (it can, but only for 5-10 seconds), which greatly limits the main functionality of some apps (e.g. iBeacon trackers).

A well-known practice is to check if the app has Always authorization, and if not, present information describing why this is important and how the user can change it (manually in settings).

But we cannot distinguish if we have Always or Provisional Always authorization status (at least directly), so the logic:

if (CLLocationManager.authorizationStatus() != .authorizedAlways) {
    // Prompt the user to change Location access to Always manually in settings
}

will not work for Provisional Always authorization status.

The solution could be to ask the user to choose Always manually in the settings before requesting location authorization, to prevent Provisional Always state from happening. I thought it was impossible without calling requestAlwaysAuthorization() first, but Tile somehow does it, as presented on the previous video.

Update:

I already have:

  • NSLocationAlwaysAndWhenInUseUsageDescription
  • NSLocationAlwaysUsageDescription
  • NSLocationWhenInUseUsageDescription

privacy keys set in Info.plist file.

KlimczakM
  • 12,576
  • 11
  • 64
  • 83

2 Answers2

0

Have you tried simply putting the NSLocationAlwaysAndWhenInUseUsageDescription key in your plist and then instantiating a CLLocationServices instance and using it to try to start a location update? I suspect this will cause the entry to appear in settings even if the location update will not function until permission is granted.

KlimczakM
  • 12,576
  • 11
  • 64
  • 83
davidgyoung
  • 63,876
  • 14
  • 121
  • 204
  • Yes, I had the keys, but starting location updates **without** asking for permission helped. Thank you! Although I'm a bit worried it may stop working after some iOS update, it doesn't sound like a feature. – KlimczakM Jan 03 '20 at 14:02
  • It's always possible Apple could make a future change here (Apple often surprises us!), but I think it is reasonably safe to use this technique, because I believe it is consistent with the way Apple wants their permission system to work -- only if an app tries to use location does the permission setting show up. Just my two cents. – davidgyoung Jan 04 '20 at 07:19
  • ***This works on iOS13 only.*** That's the risk of using undocumented behaviours... – KlimczakM Apr 28 '20 at 07:04
0

To reveal "Location" in the app settings without asking for permission first (Always requires two-step opt-in), you need to call locationManager.requestLocation().

func scheduleLocationUpdates() {
    if CLLocationManager.locationServicesEnabled() && CLLocationManager.authorizationStatus() == .authorizedAlways {
        locationManager.startUpdatingLocation()
        // hide full screen instruction (if shown)
    } else {
        if (UIDevice.current.systemVersion as NSString).floatValue >= 13.0 {
            locationManager.requestLocation() // reveal "Location" in app settings (works on iOS 13 only)
            // show full screen instruction how to provide "Always authorization"
        } else {
            if CLLocationManager.authorizationStatus() == .notDetermined {
                locationManager.requestAlwaysAuthorization()
            } else {
                // show full screen instruction how to provide "Always authorization"
            }
        }
    }
}

Function scheduleLocationUpdates() should called in viewWillAppear and after UIApplication.willEnterForegroundNotification event (e.g. when the user comes back from Settings).

On iOS 12 "Location" will not appear in the app settings without asking for the permission first. But you can ask for Always permission directly (without two steps), so this is not necessary.

KlimczakM
  • 12,576
  • 11
  • 64
  • 83