Expo background location
Long Ping runs in Expo apps that compile their own native code: a development build, or any project that uses
npx expo prebuild to generate its ios/ and android/ folders. It does not run in Expo Go.
There is no Expo config plugin, and the setup does not need one. The iOS keys go in app.json, and the Android
permissions, foreground service and boot receiver come from the library’s own manifest.
Requirements
Section titled “Requirements”- Expo SDK 52 or newer. Long Ping’s peer dependency is
react-native >= 0.76.0, and SDK 52 is the first Expo SDK on React Native 0.76. - The New Architecture. Long Ping is a TurboModule. Projects created on SDK 52 have
"newArchEnabled": trueinapp.json; from SDK 53 it is the default. - iOS 15.1+ and Android 7.0+ (API 24).
- Google Play Services on Android. Location comes from the fused location provider only.
Why not Expo Go
Section titled “Why not Expo Go”Expo Go is a prebuilt app with a fixed set of native modules compiled into it. Your JavaScript runs inside it,
but you cannot add native code to it, and its Info.plist and Android manifest belong to Expo Go, not to your
app.
Background location depends on exactly those parts: the location background mode and the usage strings on
iOS, the foreground service and its permissions on Android. Long Ping’s tracking engine is native code as well.
In Expo Go, importing react-native-long-ping throws an error saying the LongPing native module could not be
found. The fix is to build your own app binary, which is what the rest of this page does.
1. Install
Section titled “1. Install”npx expo install react-native-long-pingThere is no separate pod install step: npx expo run:ios and EAS Build install the LongPing pod when they
build.
2. Configure app.json
Section titled “2. Configure app.json”{ "expo": { "name": "Your App", "slug": "your-app", "newArchEnabled": true, "ios": { "bundleIdentifier": "com.example.yourapp", "infoPlist": { "NSLocationWhenInUseUsageDescription": "Your location is shared with dispatch while you are on duty.", "NSLocationAlwaysAndWhenInUseUsageDescription": "Your location is shared with dispatch while you are on duty, including when the app is closed.", "UIBackgroundModes": ["location"] } }, "android": { "package": "com.example.yourapp", "permissions": ["android.permission.POST_NOTIFICATIONS"] } }}The three ios.infoPlist keys are the same ones a bare React Native app adds to Info.plist in
Installation: one usage string for the “While Using” prompt, one for the
upgrade to “Always”, and the location background mode. Without the background mode, tracking stops when the
app goes to the background, and Long Ping writes an error to its on-device log. Apple reviews the usage strings,
so say who sees the location and when.
Expo writes these keys into the native projects when it generates them. If ios/ and android/ already exist
from an earlier prebuild, run npx expo prebuild again after changing app.json. If you commit the native
folders and edit them by hand, Expo no longer syncs app.json into them; put the keys straight into your
Info.plist instead.
3. Android: what comes with the library
Section titled “3. Android: what comes with the library”Gradle merges the library’s AndroidManifest.xml into your app, whatever android.permissions lists. It
brings ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION, FOREGROUND_SERVICE,
FOREGROUND_SERVICE_LOCATION, RECEIVE_BOOT_COMPLETED, WAKE_LOCK, INTERNET and ACCESS_NETWORK_STATE,
plus the foreground service (foregroundServiceType="location") and the boot receiver that restarts tracking
after a reboot or an app update.
The one Android permission you add is POST_NOTIFICATIONS, listed under android.permissions above. Tracking
runs in a foreground service, which always has a notification, and on Android 13 and newer that notification
is hidden unless the user grants this permission. Long Ping neither declares nor requests it, and Android only
shows the prompt for permissions the app declares.
If your app only tracks while it is open, add
"blockedPermissions": ["android.permission.ACCESS_BACKGROUND_LOCATION"] under android. Expo then removes
the permission from the merged manifest, the same result as the tools:node="remove" entry in
App Store & Google Play review. That page also covers what changes without it,
and the Play Console declarations you need if you keep it.
4. Ask for permission and start
Section titled “4. Ask for permission and start”import { PermissionsAndroid, Platform } from 'react-native';import LongPing, { AuthorizationStatus } from 'react-native-long-ping';
// Call at startup. Safe on every launch: it never starts, stops or prompts.export function setUpTracking() { return LongPing.configure({ movingIntervalSec: 30, parkedIntervalSec: 180, notification: { title: 'On duty', text: 'Sharing your location with dispatch' }, });}
// Call from a button, after your own screen that explains why you need location.export async function goOnDuty() { if (Platform.OS === 'android' && Platform.Version >= 33) { await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS); }
const status = await LongPing.requestPermission('always'); if (status === AuthorizationStatus.DENIED || status === AuthorizationStatus.RESTRICTED) { return false; }
await LongPing.start(); return true;}requestPermission('always') shows the operating system’s two-step flow, “While Using” first and then
“Always”; Permissions explains what each platform shows and how to read
the status. On Android it needs an Activity in the foreground, so call it from a screen, not from background
code. Without an http.url in the config, fixes are recorded on the device but not uploaded; the
Quick start adds the upload URL and your access token.
5. Build
Section titled “5. Build”# Local build on a connected device. Generates ios/ and android/ first if they are missing.npx expo run:ios --devicenpx expo run:android --device
# Or build in the cloud with EASeas build --platform ioseas build --platform androidAdding or upgrading Long Ping, or changing the keys above, needs a new build. Over-the-air updates only ship
JavaScript and assets, so they cannot add the native module to an existing build. To check the first build,
call LongPing.getState() and log the result: if it resolves, the native module is linked.