Not all who wander are lost

Kuba Gonet
Software Mansion
Published in
3 min readMar 28, 2024

--

Expo Location is a library that allows you to access the geolocation of the device. In particular, it can access the location when the application runs in the background. It’s often used to create navigation applications (like Google Maps) or fitness tracking applications (e.g. Strava).

We’ll guide you through the steps required to set up and use the background location in an Expo app.

Setup

First of all, we need to install expo-location, alongside with expo-task-manager which we’ll use to keep the location running in a background task.

npx expo install expo-location expo-task-manager

The background location requires additional permission requests, which are configured in the Info.plist for iOS and AndroidManifest.xml for Android.

In an Expo project, you configure these settings in the app.json file, in the section for additional configuration for plugins.

// app.json
// …
“plugins”: [
[
“expo-location”,
{
“isIosBackgroundLocationEnabled”: true,
“isAndroidBackgroundLocationEnabled”: true
}
]
],
// …

Permission request and task registering

Next, we need to ask the user to authorize location tracking. For background location, we need the “Always allow” option to be enabled.

This is done in two steps — ask for foreground location permission and then upgrade the permission to background location.

async function requestPermissions() {
const requestForeground = Location.requestForegroundPermissionsAsync;
const requestBackground = Location.requestBackgroundPermissionsAsync;

const foregroundRequest = await requestForeground();
if (foregroundRequest.granted) {
const backgroundRequest = await requestBackground();
if (backgroundRequest.granted) {
return true
}
}
return false
}

After doing that, we can register the task:

const LOCATION_TASK_NAME = “background-location-task”;
await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, {
accuracy: Location.Accuracy.Highest,
activityType: Location.LocationActivityType.Fitness,
});

We can customize many options, e.g. time after a batch of updates will be delivered to the callback, location accuracy, etc. You can learn about all the options from the Expo Location documentation [1], [2].

Lastly, we need to register a callback with the same name to be called when the background location update arrives. The callback must be fairly fast to preserve the battery and to fit in platform’s time constraints (e.g. on iOS background service must take at most 30s).

TaskManager.defineTask(LOCATION_TASK_NAME, saveLocationDataTask);

saveLocationDataTask is a callback that takes one argument, an object with data and error keys. The data has a following shape:

type FullLocationData = {
coords: {
altitude: number;
altitudeAccuracy: number;
latitude: number;
accuracy: number;
longitude: number;
heading: number;
speed: number;
};
timestamp: number;
};

In our example, we’re taking latitude, longitude, and accuracy and then saving it using async storage.

After adding mapbox and creating a visualisation…

function Map() {
const locations = useLocationData();
if (locations.length === 0) {
// …
}
return (
<MapView style={styles.fill} deselectAnnotationOnTap={true}>
<ShapeSource id="routeSource" shape={toRoute(locations)}>
<LineLayer id="routeFill" style={mapStyles.lineStyle} />
</ShapeSource>
<Camera defaultSettings={cameraConfig(locations[0])} />
</MapView>
);
}

function toRoute(locations: LocationData[]): LineString {
return { type: "LineString", coordinates: locations.map(toCoords) };
}

function cameraConfig(initialLocation: LocationData) {
return { centerCoordinate: toCoords(initialLocation), zoomLevel: 16 };
}

…we’re ready to supply the data. The easiest way is to use location emulation included in iOS simulator and Android emulator or using a fake GPS app on Android.

If you’re feeling adventurous, you can go outside for a walk.

Here’s a short route recorded around the Software Mansion office.

A route recorded by the app

One thing to take note of is that some Android devices such as Huawei ones have strict battery policy — this means that even with background location enabled, the app may not receive updates. Based on this StackOverflow answer, it most likely can be circumvented by disabling battery optimisations for the app.

Repository link with full example.

--

--