Available In-App APIs
Localytics provides a number of APIs within its In-App messages which can be used to prompt users for permissions:
On Android:
//Prompt the user to provide location permissions localytics.promptForLocationPermissions("action");
On iOS:
// An integer representing the current location authorization status as defined in CLAuthorizationStatus localytics.locationAuthorizationStatus //Prompt the user to provide location permissions always localytics.promptForLocationAlwaysPermissions("action"); //Prompt the user to provide location permissions when the app is in use. localytics.promptForLocationWhenInUsePermissions("action");
The above API’s will help you prompt the user, but in order to define an ideal user workflow, you may want to better understand the permission prompt process and potentially collect some additional information.
Permission Workflow
Android
Android introduced permission prompts in Marshmallow (Android 6, API level 23). Android allows the developer to request permissions from the user, and after the second request, the user has the option to suppress future permission requests.
To understand the device’s current state Android provides a few API’s:
ActivityCompat#checkSelfPermission
: This method will return a boolean indicating if the user is opted into a permission or not.ActivityCompat#shouldShowRequestPermissionRationale
: This method will return a boolean indicating if the app should attempt to show rationale behind their desire for a permission. This value is true if the user has denied the requested permission in the past, and hasn’t suppressed all future permission requests.
(More info can be found in the Android Documentation)
We can visualize this behavior in the following chart:
iOS
Starting with iOS 11, requesting location permissions from a user has been broken up into a multi-step process, as suggested by Apple. For app’s that don’t have a requirement on geofencing or background location information, Apple suggests first requesting location permissions when the app is in use, and only some time after requesting and providing those permissions should the app request location permissions always.
Apple will enforce that the customer is not overly bothered by OS prompts by only showing one prompt of each type per app install. This means that you can only prompt the user once for when in use permissions, and regardless of if they accept or reject they will never be shown the OS prompt again for this app install. Similarly, the permission prompt for location always will only be shown once, but it will also be suppressed if the user denied the when in use permission, or has gone into settings and turned off location permissions for your app.
Because of this restrictive behavior, it is imperative that you set up a proper workflow to ensure maximal permission opt-in rates. We can visualize all the possibilities in the following chart:
Triggering the In-App
In each flow chart, there is at least one dead end that results in no OS prompt for the user, despite requesting there be one. If any In-App message calls any of the permission prompt API’s when in the dead end state, the In-App will be dismissed without showing an OS prompt, resulting in bad user experience. In order to avoid these dead ends, it is up to you to only show an In-App message that attempts to request permissions when you know the user can be prompted by the OS.
Unfortunately, the state of the device at the time of potential dead end can be indistinguishable from valid prompting states when using the OS provided APIs. To ensure we know what state we are in, we need to capture one more piece of information: has the user been prompted before. We can then take advantage of this information to only show an In-App when the user can see the OS prompt.
To achieve this, we suggest triggering the permission prompt off of a specific event. Additionally, the app should keep track of whether or not the device has shown the OS level prompts for permissions in the past and add that as an attribute attached to the event (in addition to the current Location permission status):
On Android:
int locationPermissionGranted = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION); boolean shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION); attrs.put("Can Show Rationale", Boolean.toString(shouldShowRationale)); attrs.put("Has Seen Location Prompt", Boolean.toString(hasPromptedForLocationPermissions())); attrs.put("Location Permission Authorization", Boolean.toString(locationPermissionGranted == PackageManager.PERMISSION_GRANTED)); Localytics.tagEvent("location-prompt", attrs);
On iOS:
- (void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; [Localytics tagEvent:@"location-prompt" attributes:@{ @"Location Authorization":[self locationAuthorizationStatusString], @"Has Prompted For Location When In Use": [self hasPromptedForLocationWhenInUse], @"Has Prompted For Location Always": [self hasPromptedForLocationAlways], }]; } - (NSString *)locationAuthorizationStatusString { CLAuthorizationStatus status = [CLLocationManager authorizationStatus]; if (status == kCLAuthorizationStatusDenied) { return @"Denied"; } else if (status == kCLAuthorizationStatusRestricted) { return @"Restricted"; } else if (status == kCLAuthorizationStatusNotDetermined) { return @"Not Determined"; } else if (status == kCLAuthorizationStatusAuthorizedWhenInUse) { return @"When In Use"; } else if (status == kCLAuthorizationStatusAuthorizedAlways) { return @"Always"; } //Shouldn't get here, but just in case. return [NSString stringWithFormat:@"%d", status]; }
Take advantage of the events you tagged to only show the In-App to users who can see the OS prompt (This image follows the iOS workflow specifically):