'expo location permissions work on android, but not on iOS

I have a react-native app that uses expo. The location permissions work on android, but not on iOS. On a real iPhone (iOS version 14.4.2), it does nothing. On a simulator, I get this error message:

[Unhandled promise rejection: Error: LOCATION_FOREGROUND permission is required to do this operation.] at node_modules/react-native/Libraries/BatchedBridge/NativeModules.js:103:50 in promiseMethodWrapper at node_modules/@unimodules/react-native-adapter/build/NativeModulesProxy.native.js:15:23 in moduleName.methodInfo.name at http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:293333:90 in watchPositionAsync$ at http://127.0.0.1:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:293327:37 in watchPositionAsync

Update: I tried it on another Android device and it did not work, so maybe it works on my main Android phone because the permissions were already granted when running a previous version? There is a bug report about permissions breaking between Expo version 40 and 41 https://github.com/expo/expo/issues/12581

Here is what I'm using:

app.json:

{
  "expo": {
    "name": "Andiamo",
    "slug": "andiamo_places",
    "version": "1.0.0",
    "orientation": "default",
    "icon": "./assets/icon.png",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "updates": {
      "fallbackToCacheTimeout": 0
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "bundleIdentifier": "com.andiamoplaces.andiamotravelapp",
      "supportsTablet": true,
      "infoPlist": {
        "NSLocationUsageDescription":
          "The app needs permission to access your location, if you want to be located on the map.",
        "NSLocationWhenInUseUsageDescription": "The app needs permission to access your location, if you want to be located on the map",
        "NSLocationAlwaysUsageDescription": "The app needs permission to access your location, if you want to be located on the map",
        "NSLocationAlwaysAndWhenInUseUsageDescription": "The app needs permission to access your location, if you want to be located on the map",
        "NSPhotoLibraryUsageDescription": "The app needs permission to your camera roll, if you want to upload pictures.",
        "NSCameraUsageDescription": "The app needs permission to your camera, if you want to take pictures from the app."
      }
    },
    "android": {
      "package": "com.andiamoplaces.andiamotravelapp",
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#FFFFFF"
      },
      "permissions": [
        "CAMERA",
        "LOCATION"
      ]
    },
    "web": {
      "favicon": "./assets/favicon.png"
    },
    "description": ""
  }
}

package.json:

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "postinstall": "jetifier -r"
  },
  "dependencies": {
    "@expo/vector-icons": "^12.0.0",
    "@react-native-community/clipboard": "^1.5.1",
    "@react-native-community/datetimepicker": "3.2.0",
    "@react-native-community/masked-view": "0.1.10",
    "@react-navigation/native": "^3.8.4",
    "@use-expo/font": "^2.0.0",
    "cross-fetch": "^3.1.4",
    "expo": "^41.0.0",
    "expo-camera": "~11.0.2",
    "expo-cli": "^4.4.4",
    "expo-clipboard": "~1.0.2",
    "expo-facebook": "~11.0.5",
    "expo-font": "~9.1.0",
    "expo-image-manipulator": "~9.1.0",
    "expo-image-picker": "~10.1.4",
    "expo-location": "~12.0.4",
    "expo-media-library": "~12.0.2",
    "expo-permissions": "~12.0.1",
    "expo-status-bar": "~1.0.4",
    "firebase": "8.2.3",
    "geolib": "^3.3.1",
    "global": "^4.4.0",
    "luxon": "^1.26.0",
    "native-base": "^2.15.2",
    "react": "16.13.1",
    "react-devtools": "^4.13.2",
    "react-dom": "16.13.1",
    "react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz",
    "react-native-color-picker": "^0.6.0",
    "react-native-draggable-flatlist": "^2.6.1",
    "react-native-gesture-handler": "~1.10.2",
    "react-native-ionicons": "^4.6.5",
    "react-native-maps": "0.27.1",
    "react-native-material-menu": "^1.2.0",
    "react-native-modal": "^11.10.0",
    "react-native-pell-rich-editor": "^1.7.0",
    "react-native-popup-menu": "^0.15.10",
    "react-native-prompt-crossplatform": "^1.6.1",
    "react-native-ratings": "^8.0.4",
    "react-native-reanimated": "~2.1.0",
    "react-native-safe-area-context": "3.2.0",
    "react-native-safe-area-view": "^1.1.1",
    "react-native-screens": "~3.0.0",
    "react-native-simple-radio-button": "^2.7.4",
    "react-native-switch": "^2.0.0",
    "react-native-switch-selector": "^2.1.4",
    "react-native-vector-icons": "^8.1.0",
    "react-native-web": "~0.13.12",
    "react-native-webview": "11.2.3",
    "react-native-youtube-iframe": "^2.0.1",
    "react-navigation": "^4.4.4",
    "react-navigation-drawer": "^2.7.1",
    "react-navigation-stack": "^2.10.4",
    "react-navigation-tabs": "^2.11.1",
    "react-redux": "^7.2.4",
    "redux": "^4.1.0",
    "redux-persist": "^6.0.0",
    "redux-thunk": "^2.3.0"
  },
  "devDependencies": {
    "@babel/core": "~7.9.0",
    "jetifier": "^1.6.8"
  },
  "private": true
}

code:

startUserLocationUpdates = async () => {
    const returns = await Location.watchPositionAsync(
      {
        accuracy: Location.Accuracy.BestForNavigation,
        timeInterval: 1000,
        // distanceInterval: 0,
      },
      (loc) => {
        if (this.props.ShowLocation) this.centerMapTo(loc.coords.latitude, loc.coords.longitude);
        this.props.onSetCurrentLocation({ latitude: loc.coords.latitude, longitude: loc.coords.longitude });
      }
    );
    this._removeLocationUpdates = returns.remove;
  };


Solution 1:[1]

An updated version of the Permissions with formatted code.

if (Platform.OS !== "web") {
  const { status } = await Location.requestForegroundPermissionsAsync();
  
  if (status !== "granted") {
    Alert.alert(
      "Insufficient permissions!",
      "Sorry, we need location permissions to make this work!",
      [{ text: "Okay" }]
    );
    return;
  }
}

In fact, I've had the opposite scenario; iOS was working fine, but Android did not without the code above.

<uses-permission android:name=... in the AndroidManifest.xml is being automatically by Expo, so you don't need to care about it and modify anything.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Daniel Danielecki