question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[expo-location] request permission always returns denied

See original GitHub issue

Summary

If the user initially deny location permission, but consequently grant permission from settings, the location always still returns as “denied”. This problem is very similar to #15273 in expo-media-library

Steps to reproduce:

  • Clear app state so app will ask to allow location permissions
  • Deny location permissions
  • Allow location permission from the settings
  • Reopen the app => permissions is still denied
Settings App
image image

Managed or bare workflow? If you have ios/ or android/ directories in your project, the answer is bare!

managed

What platform(s) does this occur on?

Android

SDK Version (managed workflow only)

44.0.0

Environment

expo-env-info 1.0.2 environment info: System: OS: macOS 12.2.1 Shell: 5.8 - /bin/zsh Binaries: Node: 12.22.1 - ~/.nvm/versions/node/v12.22.1/bin/node Yarn: 1.22.17 - ~/.nvm/versions/node/v12.22.1/bin/yarn npm: 7.24.1 - ~/.nvm/versions/node/v12.22.1/bin/npm Watchman: 2022.03.14.00 - /usr/local/bin/watchman Managers: CocoaPods: 1.11.3 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.4, iOS 15.4, macOS 12.3, tvOS 15.4, watchOS 8.5 IDEs: Android Studio: 2021.1 AI-211.7628.21.2111.8139111 Xcode: 13.3/13E113 - /usr/bin/xcodebuild npmPackages: expo: ^44.0.0 => 44.0.6 react: 17.0.1 => 17.0.1 react-dom: 17.0.1 => 17.0.1 react-native: 0.64.3 => 0.64.3 react-native-web: 0.17.1 => 0.17.1 react-navigation: ^4.4.4 => 4.4.4 Expo Workflow: managed

Reproducible demo

https://github.com/bmamouri/expo-location

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:3
  • Comments:11 (3 by maintainers)

github_iconTop GitHub Comments

12reactions
byCedriccommented, Mar 29, 2022

Hi people! There are a couple of things going on here. It’s going to be a long explanation, but I hope it clears up things for a lot of people 😄

  1. Android option “Allow all the time” is a background permission Android tries to differentiate the two location access types with fore- and background categories (Android docs). This is important to keep in mind when requesting location permissions on Android.

  2. From Android 11, you need foreground permission before asking background permission This is quite unfortunate but Android now enforces its “best practice” of requesting permissions. If you need access to the background location, you first have to ask for foreground permissions. Without these foreground permissions, the app is blocked from the background location even though users granted them through the app settings. From their docs:

Even if several features in your app require location access, it’s likely that only some of them require background location access. Therefore, it’s recommended that your app performs incremental requests for location permissions, asking for foreground location access and then background location access. By performing incremental requests, you give users more control and transparency because they can better understand which features in your app need background location access. … The process for performing incremental requests is as follows:

  1. At first, your app should guide users to the features that require foreground location access, such as the “share location” feature in Figure 1 or the “show current location” feature in Figure 2. It’s recommended that you disable user access to features that require background location access until your app has foreground location access.

  2. At a later time, when the user explores functionality that requires background location access, you can request background location access.

  1. expo-location only registers new permissions on cold start, or through requestForegroundPermissionAsync/requestBackgroundPermissionAsync In the code example, you use the react-use useAsync hook, which is great in most cases. Unfortunately, you never start the permission request through the proper methods. When the linking page is opened, the app is put in background state. When the linking page is closed, the app is put back in foreground state without any refresh or update to it.

This basically means that there is no way for expo-location (both natively and on the JS side) to know that there has been a change in permissions. It won’t rerender at all, and useAsync won’t re-execute the Location.getForegroundPermission. Leaving you with the old permission status.

  1. Expo Go has a scoped permission system to run multiple projects During development, we use a system to “scope” permissions to certain Expo projects. Even though the user has granted full access for single permission, the project (or “experience” as we call them) needs to re-request the permission. This get’s you closer to real-world scenarios when dealing with permissions. And because of point 3, Expo Go doesn’t know you granted access to that project. That would still require a requestXPermissionAsync call to set the scoped permissions properly.

Note that when building your app, this scoped permission system is not included. It’s just for development and only inside Expo Go.


As @borasumer correctly noted, we do have permission hooks that can help you overcome most of these issues (including updating permission status whenever the user switches back from the app settings page). But, you still have to keep in mind that permissions needs to be requested through the requestXPermissionAsync methods, and Android wants a granted foreground permission first.

This code snippet should handle pretty much all edge cases for requesting these permissions
import * as Location from 'expo-location';
import {Button, Linking, StyleSheet, Text, View} from 'react-native';

export default function App() {
  const [foreground, requestForeground] = Location.useForegroundPermissions();
  const [background, requestBackground] = Location.useBackgroundPermissions();

  // In this example, we follow a couple of rules for the permissions
  //  1. Foreground permission needs to be granted before asking background permission
  //  2. Whenever the user repeatedly blocks a permission, `canAskAgain` will be false and we _have_ to open app settings
  //  3. When opening app settings, we need to manually refresh the permissions in order to update the states

  return (
    <View style={styles.container}>
      <View style={styles.block}>
        <Text>Foreground permission:</Text>
        <Text>status: {foreground?.status || 'pending'}</Text>
        {foreground && !foreground.granted && foreground.canAskAgain && (
          // If the permission is not granted, but we can request again, show this
          <Button title="Grant permission" onPress={requestForeground} />
        )}
        {foreground && !foreground.granted && !foreground.canAskAgain && (
          // If the permission is not granted, and we can't request it again, show this.
          //
          // Unfortunately, we have to manually refresh the foreground status in this case.
          // When the user comes back from the app settings page, after manually granting permission,
          // the user has to press this button again, in order to update the state of that permission.
          // We use `requestXPermissionAsync` to update the scoped permission when running in Expo Go.
          // 
          // You could try to implement appState and auto-refreshes, but for this example 
          // I just check before sending people to the app settings.
          // NOTE: this is not a great scenario to be in, and Google will have some issues with this flow.
          <Button
            title="Grant permission through settings"
            onPress={() => requestForeground().then(p => !p.granted && Linking.openSettings())}
          />
        )}
      </View>

      <View style={styles.block}>
        <Text>Background permission:</Text>
        <Text>status: {background?.status || 'pending'}</Text>
        {!foreground?.granted && (
          // We don't have the foreground permission yet,
          // which is required for Android to use background location
          <Text>Grant foreground location permission first</Text>
        )}
        {foreground?.granted && background && !background.granted && background.canAskAgain && (
          // If the permission is not granted, but we can request again, show this.
          // This handles the permission status update automatically for you, when users are coming back from the app settings
          <Button title="Grant permission" onPress={requestBackground} />
        )}
        {foreground?.granted && background && !background.granted && !background.canAskAgain && (
          // If the permission is not granted, and we can't request it again, show this.
          // Same here, we have to manually refresh the background status in this case.
          // NOTE: this is not a great scenario to be in, and Google will have some issues with this flow.
          <Button
            title="Grant permission through settings"
            onPress={() => requestBackground().then(p => !p.granted && Linking.openSettings())}
          />
        )}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  block: {
    marginVertical: 16,
  },
});

Hope this helps!

2reactions
bsegail-woolworthscommented, Mar 22, 2022

Finding the same issue with expo-camera Camera.requestCameraPermissionsAsync()

Read more comments on GitHub >

github_iconTop Results From Across the Web

React-Native/Expo Location permissions issues - Stack Overflow
I've tried using Expo permissions module like this await Expo.Permissions.getAsync(Expo.Permissions.LOCATION); but again - the same results. Am ...
Read more >
Location - Expo Documentation
Location permissions must be granted. On iOS it must be granted with Always option. (iOS only) "location" background mode must be specified in...
Read more >
How to use the expo-permissions.LOCATION function ... - Snyk
alert( "Permission Denied", "To enable location, tap Open Settings, then tap on Location, and finally tap on While Using the App.", [ {...
Read more >
Location tracking in Expo and React Native | Chafik Gharbi
User location data is considered sensitive information, that's why we need to ask permission otherwise, tracking won't even start.
Read more >
react-native-permissions - npm
By default no permission handler is installed. Update your Podfile by choosing the ones you want to check or request, then run pod...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found