react-native-background-geolocation
react-native-background-geolocation copied to clipboard
[Help Wanted] Background Location Not Updating Consistently + Offline Data Collection Issues on iOS & Android
Required Reading
- [x] Confirmed
Plugin Version
"react-native-background-geolocation": "^4.18.6",
Mobile operating-system(s)
- [x] iOS
- [x] Android
Device Manufacturer(s) and Model(s)
moto edge 40 neo
Device operating-systems(s)
android 13
React Native / Expo version
react-native: 0.73.4
What do you require assistance about?
I'm using the licensed version of react-native-background-geolocation, and I'm encountering issues where latitude and longitude stop updating for long periods (30 minutes to 1 hour). I need help ensuring reliable background tracking, especially when the app is in the background or the device is offline.
🧪 Environment Library: react-native-background-geolocation (licensed)
Platform(s): Android & iOS
React Native Version: [insert here]
Device(s): [insert model(s)]
Permissions: Location Always, Background Modes enabled
🐞 Problem Description Background location updates stop intermittently.
This happens even when background mode is enabled and location permission is granted.
When the device is offline, location data is not collected or synced later.
On iOS, it's unclear how background updates behave, especially regarding battery optimizations.
[Optional] Plugin Code and/or Config
import BackgroundGeolocation from 'react-native-background-geolocation';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createLiveTracking } from './services/hsa.service';
import { Alert } from 'react-native';
import { startBackgroundTask } from './FlightModeAndLocation'
let lastUpdateTimestamp = 0;
export async function requestLocationPermission() {
try {
const state = await BackgroundGeolocation.requestPermission();
return state === BackgroundGeolocation.AUTHORIZATION_STATUS_ALWAYS ||
state === BackgroundGeolocation.AUTHORIZATION_STATUS_WHEN_IN_USE;
} catch (error) {
console.error('Error requesting location permission:', error);
return false;
}
}
export async function getCurrentLocation() {
return new Promise((resolve, reject) => {
BackgroundGeolocation.getCurrentPosition(
{
samples: 1,
persist: false,
timeout: 5000,
},
(location) => {
resolve({
lat: location.coords.latitude,
lng: location.coords.longitude,
});
},
(error) => {
console.error('Location error:', error);
if (error === 1) {
console.log('Permission issue: Requesting location access...');
requestLocationPermission();
}
reject(false);
}
);
});
}
export async function startLocationTracing(payload) {
try {
const userId = await AsyncStorage.getItem('userId');
if (!userId) {
console.warn('User ID not found in storage.');
return;
}
BackgroundGeolocation.ready({
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
distanceFilter: 0,
locationUpdateInterval: 10000,
fastestLocationUpdateInterval: 10000,
stopOnTerminate: false,
startOnBoot: true,
foregroundService: true,
enableHeadless: true,
heartbeatInterval: 10,
allowIdenticalLocations: true,
preventSuspend: true,
locationAuthorizationRequest: 'Always',
debug: false,
logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE,
}).then((state) => {
if (!state.enabled) {
BackgroundGeolocation.start().then(() => {
console.log("- Start success");
Alert.alert("Success", "BackgroundGeolocation started successfully!");
}).catch((error) => {
console.error("- Start failed: ", error);
});
}
});
const handleLocationUpdate = async (eventType) => {
const now = Date.now();
if (now - lastUpdateTimestamp >= 10000) { // 10 seconds
lastUpdateTimestamp = now;
const location = await BackgroundGeolocation.getCurrentPosition({
samples: 1,
persist: false,
});
const data = {
latitude: location.coords.latitude,
longitude: location.coords.longitude,
user: userId,
dateTime: location.timestamp,
...payload,
};
// console.log(`[${eventType}] Sending location data to server:`, data);
await createLiveTracking(data);
await AsyncStorage.setItem('locationPayload', JSON.stringify(payload));
startBackgroundTask(payload);
} else {
// console.log(`[${eventType}] Skipped - Throttled`);
}
};
BackgroundGeolocation.onHeartbeat(() => handleLocationUpdate('onHeartbeat'));
BackgroundGeolocation.onMotionChange(() => handleLocationUpdate('onMotionChange'));
BackgroundGeolocation.onLocation(() => handleLocationUpdate('onLocation'));
} catch (error) {
console.error('Error starting location tracing:', error);
}
}
let locationInterval = null;
export function stopLocationTracing() {
if (locationInterval) {
clearInterval(locationInterval);
locationInterval = null;
}
BackgroundGeolocation.stop();
console.log('Location tracking stopped.');
}
[Optional] Relevant log output
// BackgroundGeolocationHeadlessTask.js
import BackgroundGeolocation from 'react-native-background-geolocation';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { createLiveTracking } from './services/hsa.service';
import { startBackgroundTask } from './FlightModeAndLocation'
let lastUpdateTimestamp = 0;
const BackgroundGeolocationHeadlessTask = async (event) => {
console.log('[HeadlessTask] Event:', event.name);
switch (event.name) {
case 'heartbeat':
case 'location':
case 'motionchange':
try {
startBackgroundTask();
const userId = await AsyncStorage.getItem('userId');
if (!userId) {
console.warn('Headless: No user ID found.');
return;
}
const now = Date.now();
if (now - lastUpdateTimestamp < 10000) return; // Throttle to 10 seconds
lastUpdateTimestamp = now;
let location = event.params;
// If location is not passed with the event (e.g., heartbeat), manually fetch it
if (!location || !location.coords) {
console.log('[Headless] Fetching current position...');
location = await BackgroundGeolocation.getCurrentPosition({
samples: 1,
persist: false,
timeout: 5000,
});
}
const data = {
latitude: location.coords.latitude,
longitude: location.coords.longitude,
user: userId,
dateTime: location.timestamp,
};
console.log('[Headless] Sending to server:', data);
await createLiveTracking(data);
} catch (error) {
console.error('[Headless] Error:', error);
}
break;
default:
console.log('[Headless] Unhandled event:', event.name);
}
};
export default BackgroundGeolocationHeadlessTask;