skip to content

In an Expo project, how do you set the iOS usage text shown in expo-location's permission prompt, and when does a change reach users?

level: middleimportance: should knowfreq 30%

answer

  1. text lives in Info.plist
  2. set through the package's plugin options
  3. locationWhenInUsePermission
  4. false removes the key
  5. build-time, so a new binary

basics

~20 s

Pass expo-location's config plugin options in app config, such as locationWhenInUsePermission, which prebuild writes into Info.plist as NSLocationWhenInUseUsageDescription. It is native configuration, so the change ships only in a new build, never over the air.

solid answer

~40 s

The sentence iOS shows under a permission prompt is a usage description in `Info.plist`, for location `NSLocationWhenInUseUsageDescription`. In an Expo project using Continuous Native Generation I set it through the package's config plugin: `["expo-location", { "locationWhenInUsePermission": "..." }]` in the `plugins` array. Prebuild writes it into `Info.plist`; if I omit the option, the plugin keeps a value already in `Info.plist` and otherwise writes a generic default, and `false` deletes the key. `ios.infoPlist` in app config also works. Because the string is baked into the native binary, a change needs a new build and store submission, not an EAS Update. In Expo Go the prompt uses Expo Go's own text, so the custom string shows only in a development or release build.

code

json · 12 lines
json
{
  "expo": {
    "plugins": [
      [
        "expo-location",
        {
          "locationWhenInUsePermission": "Show cafes within walking distance of you."
        }
      ]
    ]
  }
}

go deeper

for a junior

Recall that the iOS prompt text lives in Info.plist and that Expo sets it through the package's plugin options in app config.

for a middle

Explain how the plugin resolves each key, option then existing value then default, what false does, and why Expo Go shows different text.

for a senior

Show why a wording change is a native release, not an update, and how a deleted or missing key turns into a runtime error on iOS.

for a principal

Treat permission copy as part of release planning: it is reviewed, localised and shipped with the binary, so bundle it with the native change that needs it.

## Where the prompt text lives On iOS the sentence under a system permission prompt is not supplied by JavaScript. It is a **usage description**, a key in the app's **`Info.plist`** that the operating system reads when the app asks. For location the keys are: - **`NSLocationWhenInUseUsageDescription`**: foreground ("while using the app") access, the one a nearby-cafes feature needs; - **`NSLocationAlwaysAndWhenInUseUsageDescription`**: shown when the app asks for background ("always") access; - **`NSLocationAlwaysUsageDescription`**: an older key the plugin still writes; the Expo docs mark its plugin option, `locationAlwaysPermission`, as deprecated. Other packages follow the same pattern: `expo-camera` sets `NSCameraUsageDescription` and `NSMicrophoneUsageDescription`. ## Setting it with the package's config plugin In a project that uses **Continuous Native Generation** (the native folders are generated by `npx expo prebuild`), each Expo package ships a **config plugin** whose options map to these keys. For `expo-location`: | plugin option | Info.plist key | |---|---| | `locationWhenInUsePermission` | `NSLocationWhenInUseUsageDescription` | | `locationAlwaysAndWhenInUsePermission` | `NSLocationAlwaysAndWhenInUseUsageDescription` | | `locationAlwaysPermission` | `NSLocationAlwaysUsageDescription` | | `motionUsagePermission` | `NSMotionUsageDescription` | You add the package with options to the `plugins` array of `app.json` or `app.config.ts`. The shared helper behind these options, `IOSConfig.Permissions.createPermissionsPlugin` in `@expo/config-plugins`, resolves each key in a fixed order: 1. **Your option**, when it is a non-empty string, wins. 2. Otherwise a **value already in `Info.plist`** (for example one set through `ios.infoPlist`) is kept. 3. Otherwise the plugin writes its **generic default**, a sentence built around `$(PRODUCT_NAME)`. 4. An option set to **`false`** deletes the key instead. The generic default exists so the prompt is never blank, but the Expo permissions guide notes that default messages will most likely need tailoring for the app to be accepted by the App Store. A specific sentence ("Show cafes within walking distance of you") also helps the user say yes. ## What happens when the key is missing If a development or release build lacks the location usage keys, `expo-location` cannot prompt properly. Its iOS requester checks for them: a missing `NSLocationWhenInUseUsageDescription` makes the foreground request reject with an `ERR_LOCATION_INFO_PLIST` error naming the key, and a build with no location usage key at all hits a fatal error whose message says the usage descriptions are missing. Deleting a key with `false` is only safe when the app never requests that capability. ## When a change reaches users The usage text is **native configuration**, not JavaScript: - **A new native build is required.** Prebuild writes `Info.plist` and the build compiles it into the binary; the Expo permissions guide states that `Info.plist` changes cannot be updated over the air. - **An EAS Update cannot change it.** An update replaces the JavaScript bundle and assets only. - **Expo Go ignores it.** Expo Go is a prebuilt app with its own usage text, so your string appears only in a development build or a store build. ## Writing a usage string that gets a yes The text is the only sentence of yours the user reads inside the system prompt, so: - **Name the feature and the benefit**: "Show cafes within walking distance of you" beats "This app needs your location". - **Match what the app does**: a foreground-only feature should not carry a background-access explanation. - **Localise it** along with the rest of the app's native strings, since the prompt appears in the device language. - **Keep one owner**: set it either in the plugin option or in `ios.infoPlist`, not both, so the resolution order never surprises a teammate. ## Android is different Android has no usage-description string in the manifest. The `expo-location` plugin adds the location permissions (`ACCESS_COARSE_LOCATION`, `ACCESS_FINE_LOCATION`, plus background or foreground-service permissions when their flags are on), and the in-app explanation before the request is your own UI. The manifest mechanics themselves belong to Android's side of the tree. ## Summary Put the text in the package's plugin options, prefer a specific sentence to the default, never delete a key the app still requests, and treat every change as a native build.

  • Can you ship a reworded location prompt with an EAS Update?
    No. The text is an `Info.plist` usage description compiled into the binary, and an update only replaces the JavaScript bundle and assets. Rewording needs a new native build and a store submission; the running JavaScript cannot change what the system prompt says.
  • When is setting an expo-location usage option to false appropriate?
    Only when the app never requests that kind of access. For example, `locationAlwaysAndWhenInUsePermission: false` removes the background key from an app that only uses foreground location. Removing the key for a capability the app still requests makes the request fail with an error naming the missing usage description.

saying these in an interview costs you the question

  • The prompt text is passed as an argument to requestForegroundPermissionsAsync.
  • Updating the usage string in app.json reaches users through an EAS Update.
  • The default usage string is always fine for App Store submission.
  • Setting a plugin option to false disables the permission request at runtime.
  • Android shows the same usage string from the manifest in its prompt.