Working with Location (expo-location)
1. Requesting Location Permissions
| Permission | Description | Status | Usage |
|---|---|---|---|
| ACCESS_FINE_LOCATION | Allows access to precise location data | NEW | Required for high-accuracy geolocation |
| ACCESS_COARSE_LOCATION | Allows access to approximate location data | STABLE | Used for less precise location services |
Example: Requesting Location Permissions
import * as Permissions from 'expo-permissions';
import * as Location from 'expo-location';
const requestLocationPermission = async () => {
const { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
console.log('Permission to access location was denied');
return false;
}
return true;
};
Note: On Android, you must also add the
ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions to your app.json or app.config.js.
Warning: Users may deny location permissions, which can lead to incomplete or inaccurate geolocation data. Always handle permission denials gracefully.
2. Getting Current Position
| Property | Description | Default | Notes |
|---|---|---|---|
| accuracy | Accuracy level of the location data | 10 |
Higher values mean more accurate but slower results |
| distanceInterval | Minimum distance (in meters) to travel before updating location | 0 |
Set to 0 for updates on every movement |
| heading | Include heading information in the result | false |
Requires additional permissions on some platforms |
Example: Requesting Current Position
import * as Location from 'expo-location';
const getCurrentLocation = async () => {
try {
const { coords } = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
distanceInterval: 10,
});
console.log("Latitude:", coords.latitude);
console.log("Longitude:", coords.longitude);
} catch (error) {
console.error("Error getting location:", error);
}
};
getCurrentLocation();
Note: The user must grant location permissions for this to work. On iOS, you need to add
NSLocationWhenInUseUsageDescription or NSLocationAlwaysAndWhenInUseUsageDescription to your Info.plist.
Warning: On Android, you must request
ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions at runtime.
3. Watching Position Changes
| Property | Description | Values |
|---|---|---|
| watchPositionAsync | Starts watching for position changes | Promise<Position> |
| watchPositionAsync(options) | Options object to configure watching | Object |
Example: Watching Position Changes
import * as Location from 'expo-location';
const watchPosition = async () => {
const { coords } = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
distanceInterval: 10,
timeInterval: 5000,
},
(location) => {
console.log("New location:", location.coords);
}
);
};
watchPosition();
Note: The callback function will be called every time the location changes according to the specified interval.
Warning: Make sure to stop watching when no longer needed using
Location.stopWatchingPositionAsync to prevent memory leaks.
| Option | Description | Default Value | Notes |
|---|---|---|---|
| accuracy | Desired accuracy level | Location.Accuracy.High |
Use Location.Accuracy.Balanced for lower power usage |
| distanceInterval | Minimum distance in meters to trigger a new position update | 10 |
Set to 0 for updates on every change |
| timeInterval | Minimum time in milliseconds between updates | 5000 |
Set to 0 for updates on every change |
4. Reverse Geocoding
Reverse geocoding is the process of converting geographic coordinates (latitude and longitude) into human-readable addresses.
| Property | Description | Example |
|---|---|---|
| location | Object containing latitude and longitude | { latitude: 40.7128, longitude: -74.0060 } |
| locale | Locale to use for address formatting (optional) | 'en-US' |
Example: Reverse Geocoding
import * as Location from 'expo-location';
const address = await Location.reverseGeocodeAsync({
latitude: 40.7128,
longitude: -74.0060,
});
console.log(address[0].formattedAddress);
// Output: "123 Main St, New York, NY 10001, USA"
Note: The
reverseGeocodeAsync function returns an array of address objects. The first object in the array is typically the most accurate match.
5. Forward Geocoding
| Property | Description | Values |
|---|---|---|
| address | Address to geocode | "1600 Amphitheatre Parkway, Mountain View, CA" |
| latitude | Latitude of the location | 37.4223 |
| longitude | Longitude of the location | -122.0843 |
Example: Forward Geocoding an Address
import * as Location from 'expo-location';
const address = '1600 Amphitheatre Parkway, Mountain View, CA';
const geocoded = await Location.geocodeAsync(address);
console.log(geocoded[0]); // First geocoded result
Note: The
geocodeAsync function returns an array of geocoded results. Each result includes latitude, longitude, and address components.
6. Setting Location Accuracy
| Accuracy Level | Description | Use Case | Notes |
|---|---|---|---|
| HIGH_ACCURACY | Uses GPS and network-based location sources for the most precise location data. | Navigation, mapping, real-time tracking | May drain battery faster |
| MEDIUM_ACCURACY | Uses GPS and network-based location sources, but with reduced precision for better battery life. | General purpose apps, weather, local search | Recommended for most use cases |
| LOW_ACCURACY | Uses network-based location sources only (e.g., WiFi, cellular towers). | Basic location services, app launch tracking | Least battery drain, least accurate |
| PASSIVE | Uses location data from other apps running on the device. | Background tracking, analytics | No direct access to GPS; relies on other apps |
Example: Setting Location Accuracy
import * as Location from 'expo-location';
await Location.requestPermissionsAsync();
const location = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
console.log(location.coords.latitude, location.coords.longitude);
Note: The
accuracy property is part of the LocationOptions object used in methods like getCurrentPositionAsync and watchPositionAsync.
7. Requesting Background Permissions
| Permission | Description | Required in Manifest | Notes |
|---|---|---|---|
| ACCESS_FINE_LOCATION | Allows access to precise location data | Yes | Required for background location access |
| ACCESS_COARSE_LOCATION | Allows access to approximate location data | Yes | May be sufficient for some background use cases |
| ACCESS_BACKGROUND_LOCATION | Allows access to location in background | Yes | Must be declared in manifest for background use |
Example: Requesting Background Location Permission
import * as Location from 'expo-location';
const requestBackgroundLocationPermission = async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
console.log('Permission to access location was denied');
return;
}
// For background access, you may need to request separately
const backgroundStatus = await Location.requestBackgroundPermissionsAsync();
if (backgroundStatus.status !== 'granted') {
console.log('Permission to access location in background was denied');
}
};
Note: On Android, background location permissions require additional setup in
AndroidManifest.xml and may need to be requested separately from foreground permissions.
Warning: Users must explicitly grant background location permission in system settings. The app cannot request it directly if the user has previously denied it.
8. Starting Location Updates
| Method | Description | Notes |
|---|---|---|
| Location.watchPositionAsync() | Starts watching for location updates | Returns a subscription object to stop watching |
Example: Starting Location Updates
import * as Location from 'expo-location';
const startLocationUpdates = async () => {
const { granted } = await Location.requestForegroundPermissionsAsync();
if (!granted) {
console.log('Permission denied');
return;
}
const subscription = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
distanceInterval: 10,
timeInterval: 1000,
},
(location) => {
console.log('New location:', location.coords);
}
);
return subscription;
};
startLocationUpdates();
Note: Ensure you have the necessary permissions declared in your app's manifest and that the user has granted location access.
9. Getting Last Known Position
| Property | Description | Example |
|---|---|---|
| getLastKnownPositionAsync | Retrieves the last known position of the device | |
Note: The last known position may be outdated or unavailable if the device has not moved recently.
10. Checking Location Services
| Property | Description | Values |
|---|---|---|
| hasLocationPermission | Indicates if the app has location permission | true or false |
| hasBackgroundLocationPermission | Indicates if the app has background location permission | true or false |
| hasFineLocationPermission | Indicates if the app has fine location permission | true or false |
| hasCoarseLocationPermission | Indicates if the app has coarse location permission | true or false |
Example: Check Location Permissions
import * as Location from 'expo-location';
const checkLocationPermissions = async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
console.log("Location permission status:", status);
};
checkLocationPermissions();
Note: On Android, you must request
ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions in your app's manifest.
Warning: On iOS, location permissions are requested at runtime. If the user denies permission, you must guide them to enable it in Settings.
11. Handling Location Errors
| Error Type | Description | How to Handle |
|---|---|---|
| PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION | Permission denied for fine location access | Request permission again or guide user to enable in settings |
| PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION | Permission denied for coarse location access | Request permission again or guide user to enable in settings |
| ExpoLocation.PermissionDeniedError | User denied location permission | Inform user and provide steps to enable permissions |
| ExpoLocation.PositionUnavailableError | Location is unavailable (e.g., GPS is off) | Inform user to enable location services |
| ExpoLocation.TimeoutError | Location request timed out | Retry or inform user to try again later |
Example: Handling Location Errors
import * as Location from 'expo-location';
try {
const location = await Location.getCurrentPositionAsync({});
} catch (error) {
if (error.code === 'PERMISSION_DENIED') {
console.log("Location permission denied");
} else if (error.code === 'POSITION_UNAVAILABLE') {
console.log("Location is unavailable");
} else if (error.code === 'TIMEOUT') {
console.log("Location request timed out");
} else {
console.log("Unknown error: ", error.message);
}
}
12. Using Heading Updates
| Property | Description | Notes |
|---|---|---|
| heading | number |
Current heading in degrees (0-360) |
| magneticHeading | number |
Heading relative to magnetic north |
| trueHeading | number |
Heading relative to true north |
| accuracy | number |
Estimated accuracy of heading in degrees |
Example: Subscribe to Heading Updates
import * as Location from 'expo-location';
const subscribeToHeading = async () => {
const { granted } = await Location.requestForegroundPermissionsAsync();
if (!granted) {
console.log("Permission denied");
return;
}
const subscription = await Location.watchHeadingAsync((heading) => {
console.log("Heading:", heading);
});
// To stop watching:
// subscription.remove();
};
subscribeToHeading();
Note: Heading updates are only available on devices with a compass sensor. On devices without a compass, the heading will be
null.