react-native-background-geolocation
react-native-background-geolocation copied to clipboard
[Help Wanted]: Delay in First Location Update After Starting Trip – Takes 30 Seconds
Required Reading
- [x] Confirmed
Plugin Version
"react-native-background-geolocation": "^4.18.7"
Mobile operating-system(s)
- [x] iOS
- [ ] Android
Device Manufacturer(s) and Model(s)
iPhone 15 Plus
Device operating-systems(s)
iOS 18.5
React Native / Expo version
0.74.1
What do you require assistance about?
Subject: iOS 18.1 – 30-Second Delay for First Location Update Despite All Permissions Granted (Logs #Attached)
I am experiencing a significant delay in receiving the first location update after starting a trip using the background geolocation plugin. Specifically, it takes around 30 seconds for the first update to be received, which is affecting the user experience.
Details: Issue: After starting a trip, the first location update is delayed by about 30 [seconds.]
Expected: The first location update should be received much sooner after starting the trip.
Observed: As shown in the attached logs, there is a gap of approximately 30 seconds between the initial trip start and the next location update.
Log Evidence: At "formatted_datetime": "06-06-2025 10:43:37 AM", the trip starts and the first location is logged.
The next significant location update does not occur until "formatted_datetime": "06-06-2025 10:44:06 AM", which is about 30 seconds later.
This gap is consistent and reproducible. Here is the log while tracking.
{
"formatted_datetime": "06-06-2025 10:43:37 AM",
"output_log": {
"odometer": 20.9,
"event": "motionchange",
"age": 837,
"uuid": "8D3E4919-E52E-4C91-98F7-9832B17966A4",
"is_moving": false,
"timestamp": "2025-06-06T10:43:37.018Z",
"coords": {
"altitude": 38.84,
"latitude": 22.69163226710883,
"heading": 213.4,
"altitude_accuracy": 3,
"heading_accuracy": 52.68,
"ellipsoidal_altitude": -17.78,
"accuracy": 14,
"speed_accuracy": 0.73,
"longitude": 72.8616261017256,
"speed": 0.46,
"floor": null
},
"extras": {},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "unknown"
}
}
},
{
"formatted_datetime": "06-06-2025 10:43:37 AM",
"output_log": {
"odometer": 20.9,
"age": 0,
"uuid": "54973471-01CF-41E0-BAF6-4D453FBE4655",
"is_moving": false,
"sample": true,
"extras": {},
"timestamp": "2025-06-06T10:43:37.846Z",
"coords": {
"altitude": 38.84,
"latitude": 22.69163226710883,
"heading": 213.4,
"altitude_accuracy": 3,
"heading_accuracy": 52.68,
"ellipsoidal_altitude": -17.78,
"accuracy": 14,
"speed_accuracy": 0.73,
"longitude": 72.8616261017256,
"speed": 0.46,
"floor": null
},
"battery": {
"is_charging": false,
"level": -1
},
"activity": {
"confidence": 100,
"type": "unknown"
}
}
},
{
"formatted_datetime": "06-06-2025 10:44:06 AM",
"output_log": {
"odometer": 20.9,
"age": 0,
"uuid": "1CA8503F-6C41-495E-A59E-BF585E40EE5D",
"is_moving": true,
"sample": true,
"extras": {},
"timestamp": "2025-06-06T10:44:06.216Z",
"coords": {
"altitude": 39.54,
"latitude": 22.691409957792207,
"heading": -1,
"altitude_accuracy": 30,
"heading_accuracy": -1,
"ellipsoidal_altitude": -17.08,
"accuracy": 6.86,
"speed_accuracy": -1,
"longitude": 72.86182643809002,
"speed": -1,
"floor": null
},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "on_foot"
}
}
},
{
"formatted_datetime": "06-06-2025 10:44:06 AM",
"output_log": {
"odometer": 53,
"event": "motionchange",
"age": 34,
"uuid": "010C0113-8A26-48EB-8746-D822CFA14F09",
"is_moving": true,
"timestamp": "2025-06-06T10:44:06.196Z",
"coords": {
"altitude": 39.54,
"latitude": 22.691409957792207,
"heading": -1,
"altitude_accuracy": 30,
"heading_accuracy": -1,
"ellipsoidal_altitude": -17.08,
"accuracy": 6.86,
"speed_accuracy": -1,
"longitude": 72.86182643809002,
"speed": -1,
"floor": null
},
"extras": {},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "on_foot"
}
}
},
{
"formatted_datetime": "06-06-2025 10:44:08 AM",
"output_log": {
"odometer": 53,
"age": 37,
"uuid": "EBED245A-ED37-4401-B7E5-B14AE5E977E9",
"is_moving": true,
"timestamp": "2025-06-06T10:44:08.000Z",
"coords": {
"altitude": 38.63,
"latitude": 22.691388856640728,
"heading": 169.1,
"altitude_accuracy": 8,
"heading_accuracy": 58.19,
"ellipsoidal_altitude": -17.98,
"accuracy": 14,
"speed_accuracy": 0.8,
"longitude": 72.86181259907117,
"speed": 0.7,
"floor": null
},
"extras": {},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "on_foot"
}
}
},
{
"formatted_datetime": "06-06-2025 10:44:09 AM",
"output_log": {
"odometer": 53,
"age": 35,
"uuid": "4C80ADE1-53FA-406B-954C-BEF8879EAB0F",
"is_moving": true,
"timestamp": "2025-06-06T10:44:09.000Z",
"coords": {
"altitude": 39.53,
"latitude": 22.69137649333355,
"heading": 171.91,
"altitude_accuracy": 8,
"heading_accuracy": 72.66,
"ellipsoidal_altitude": -17.09,
"accuracy": 14,
"speed_accuracy": 0.89,
"longitude": 72.86180723465314,
"speed": 0.7,
"floor": null
},
"extras": {},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "on_foot"
}
}
},
{
"formatted_datetime": "06-06-2025 10:44:10 AM",
"output_log": {
"odometer": 58.7,
"age": 57,
"uuid": "476EC24F-CF9A-4A66-A62B-8259C9CE4157",
"is_moving": true,
"timestamp": "2025-06-06T10:44:10.000Z",
"coords": {
"altitude": 39.17,
"latitude": 22.691365429221364,
"heading": 168.05,
"altitude_accuracy": 6,
"heading_accuracy": 54.73,
"ellipsoidal_altitude": -17.45,
"accuracy": 4.44,
"speed_accuracy": 0.78,
"longitude": 72.861798936569,
"speed": 0.7,
"floor": null
},
"extras": {},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "on_foot"
}
}
},
{
"formatted_datetime": "06-06-2025 10:44:11 AM",
"output_log": {
"odometer": 58.7,
"age": 33,
"uuid": "B0EBB294-04A3-4258-8FBF-75AAFF68679D",
"is_moving": true,
"timestamp": "2025-06-06T10:44:11.000Z",
"coords": {
"altitude": 39.06,
"latitude": 22.691353401190312,
"heading": 179.3,
"altitude_accuracy": 6,
"heading_accuracy": 49.59,
"ellipsoidal_altitude": -17.56,
"accuracy": 4.45,
"speed_accuracy": 0.83,
"longitude": 72.86179122521808,
"speed": 0.75,
"floor": null
},
"extras": {},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "on_foot"
}
}
},
{
"formatted_datetime": "06-06-2025 10:44:12 AM",
"output_log": {
"odometer": 58.7,
"age": 34,
"uuid": "298461E9-105C-482A-831A-0651BED5F105",
"is_moving": true,
"timestamp": "2025-06-06T10:44:12.000Z",
"coords": {
"altitude": 38.52,
"latitude": 22.69134179225442,
"heading": 184.57,
"altitude_accuracy": 6,
"heading_accuracy": 47.33,
"ellipsoidal_altitude": -18.09,
"accuracy": 4.33,
"speed_accuracy": 0.82,
"longitude": 72.86178376532426,
"speed": 0.98,
"floor": null
},
"extras": {},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "on_foot"
}
}
},
{
"formatted_datetime": "06-06-2025 10:44:13 AM",
"output_log": {
"odometer": 63,
"age": 36,
"uuid": "CAE8C240-A8D9-4B10-8249-F546FB2C1B77",
"is_moving": true,
"timestamp": "2025-06-06T10:44:13.000Z",
"coords": {
"altitude": 37.78,
"latitude": 22.691331692061098,
"heading": 190.55,
"altitude_accuracy": 6,
"heading_accuracy": 49.22,
"ellipsoidal_altitude": -18.84,
"accuracy": 4.04,
"speed_accuracy": 0.78,
"longitude": 72.86177689216366,
"speed": 0.98,
"floor": null
},
"extras": {},
"battery": {
"is_charging": false,
"level": 0.6499999761581421
},
"activity": {
"confidence": 100,
"type": "on_foot"
}
}
}
] ```
### [Optional] Plugin Code and/or Config
```typescript
[Optional] Relevant log output
This is my code that I used for tracking the trip
const warmUpBackgroundGeolocation = async () => {
try {
await BackgroundGeolocation.ready(
{
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_LOW,
distanceFilter: 100, // Large value to minimize battery use
stationaryRadius: 50,
locationUpdateInterval: 60000, // 1 minute
fastestLocationUpdateInterval: 30000,
stopOnStationary: true,
locationAuthorizationRequest: 'WhenInUse',
allowsBackgroundLocationUpdates: false,
pausesLocationUpdatesAutomatically: true,
debug: false,
stopOnTerminate: false,
startOnBoot: false,
foregroundService: false,
enableHeadless: false,
},
state => {
if (!state.enabled) {
BackgroundGeolocation.start();
}
},
);
} catch (error) {
console.error('Error warming up background geolocation:', error);
sendErrorReport(error, 'warmup_bg_geolocation_error');
}
};
// Keep GPS warm with periodic checks
useEffect(() => {
let intervalId;
if (!isTimerStart && !isPause) {
intervalId = setInterval(() => {
getCurrentLocation();
}, 30000); // Every 30 seconds
}
return () => clearInterval(intervalId);
}, [isTimerStart, isPause]);
useEffect(() => {
getCurrentLocation();
warmUpBackgroundGeolocation();
}, []);
// Initialize BackgroundGeolocation
const initializeBackgroundGeolocation = async () => {
if (isInitializingRef.current) return;
isInitializingRef.current = true;
try {
await BackgroundGeolocation.setConfig({
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
distanceFilter: 1,
stationaryRadius: 2,
locationUpdateInterval: 1000,
fastestLocationUpdateInterval: 500,
activityRecognitionInterval: 1000,
disableMotionActivityUpdates: false,
stopOnStationary: true,
preventSuspend: true,
locationAuthorizationRequest: 'Always',
allowsBackgroundLocationUpdates: true,
pausesLocationUpdatesAutomatically: false,
showsBackgroundLocationIndicator: true,
debug: false,
stopOnTerminate: false,
startOnBoot: true,
foregroundService: true,
enableHeadless: true,
activityType: BackgroundGeolocation.ACTIVITY_TYPE_OTHER_NAVIGATION,
});
// Ensure tracking is started in moving state
BackgroundGeolocation.start({isMoving: true});
// Set up location listener
await BackgroundGeolocation.onLocation(
location => {
updatePosition(location.coords);
sendErrorReport(location, 'onLocation_trip_tracking');
},
error => {
console.log('[onLocation] ERROR:', error);
sendErrorReport(error, 'onLocation_error_trip_tracking');
},
);
isInitializingRef.current = false;
} catch (error) {
sendErrorReport(error, 'init_bg_geolocation_error');
isInitializingRef.current = false;
}
};
// Calculate distance between two points in meters
const getDistance = (point1, point2) => {
const R = 6371e3; // Earth's radius in meters
const φ1 = (point1.latitude * Math.PI) / 180;
const φ2 = (point2.latitude * Math.PI) / 180;
const Δφ = ((point2.latitude - point1.latitude) * Math.PI) / 180;
const Δλ = ((point2.longitude - point1.longitude) * Math.PI) / 180;
const a =
Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c; // Distance in meters
};
const updatePosition = useCallback(
debounce(position => {
const {latitude, longitude, heading, speed, accuracy} = position;
const newCoordinate = {latitude, longitude};
sendErrorReport(heading, 'heading_location_update');
sendErrorReport(speed, 'speed_locationn_update');
const currentTime = Date.now();
// Filter low-accuracy locations
if (accuracy > 20) {
console.log('Ignoring location: low accuracy', accuracy);
return;
}
// Update user heading if available
if (heading >= 0) {
setUserHeading(heading);
} else if (lastSavedPosition.current) {
const calculatedHeading = calculateBearing(
lastSavedPosition.current,
newCoordinate,
);
setUserHeading(calculatedHeading);
}
// Handle speed from GPS
if (speed === -1) {
setAverageSpeed(0);
} else {
const speedKmh = speed * 3.6;
setAverageSpeed(speedKmh);
}
if (!lastSavedPosition.current) {
lastSavedPosition.current = newCoordinate;
lastUpdateTime.current = currentTime;
setStartTime(currentTime);
saveNewCoordinate(newCoordinate, 'initial');
setCurrentPosition(newCoordinate);
setRegion({
latitude: newCoordinate.latitude,
longitude: newCoordinate.longitude,
latitudeDelta: 0.0009,
longitudeDelta: 0.0009,
});
return;
}
// Calculate distance between last saved position and new position
const distanceInMeters = getDistance(
lastSavedPosition.current,
newCoordinate,
);
sendErrorReport(distanceInMeters, 'distanceInMeters_location_update');
if (distanceInMeters > 1) {
// Only update if moved more than 2 meters
const distance = tripDistance + distanceInMeters;
sendErrorReport(distance, 'distance_update');
dispatch(setTripDistance(distance));
lastSavedPosition.current = newCoordinate;
lastSavedPosition.current = newCoordinate;
saveNewCoordinate(newCoordinate, 'update');
setCurrentPosition(newCoordinate);
setRegion(prev => ({
latitude: newCoordinate.latitude,
longitude: newCoordinate.longitude,
latitudeDelta: prev?.latitudeDelta || 0.0009,
longitudeDelta: prev?.longitudeDelta || 0.0009,
}));
}
}, 100),
[tripDistance, startTime, isLandscape, averageSpeed],
);
// Clean up BackgroundGeolocation
const cleanupBackgroundGeolocation = async () => {
try {
await BackgroundGeolocation.stop();
await BackgroundGeolocation.removeListeners();
console.log('BackgroundGeolocation stopped and listeners removed');
} catch (error) {
console.error('Error cleaning up BackgroundGeolocation:', error);
sendErrorReport(error, 'cleanup_bg_geolocation_error');
}
};
useEffect(() => {
if (isTimerStart && !isPause) {
initializeBackgroundGeolocation();
BackgroundGeolocation.getCurrentPosition({
persist: false,
samples: 1,
timeout: 30, // seconds
maximumAge: 1000,
desiredAccuracy: 10,
})
.then(location => {
console.log('[getCurrentPosition]', location);
// Optional: show "trip started" indicator now
})
.catch(error => {
console.warn('[getCurrentPosition] ERROR', error);
});
} else {
cleanupBackgroundGeolocation();
}
return () => {
cleanupBackgroundGeolocation();
};
}, [isTimerStart, isPause]);```
Please learn to syntax highlight multiline code blocks. Edit your post.
The issue template provided fields to post your Config/Code into so it could automatically syntax highlight for you, but you just ignored that.
https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks
The Code format has been edited. Please review it and provide an update.
There's so much wrong with your code.
-
Read the Philosophy of Operation, especially the following section:
-
Also read the API docs
Config.stationaryRadius
To transition from the stationary state -> moving state, an iOS device must move at least 200 meters. Or you manually execute .changePace(true) while your app is in the foreground.
- You must NEVER call any method which accesses the location API (eg:
.getCurrentPosition) **before calling.ready(config), as warned in the README
useEffect(() => {
getCurrentLocation(); // <----------- NO! NO! NO! NO! NO!
warmUpBackgroundGeolocation();
}, []);
- Always register event-listeners BEFORE calling
.ready(config). - You are not required to add event-listeners each time you call
.start()and remove them when calling.stop(). Event listeners are like speakers of a stereo system — Do you disconnect your speakers each time your turn off your stereo system?? No! People connect their speakers forever. Subscribe to event-listeners once (before calling.ready(config)for the entire life-time of your app. They are harmless, nothing more than aFunctionheld in anArray. - Learn to observe the plugin's incredibly verbose logging system, configure for verbose logging and use the
debug: truefeature. See Wiki "Debugging"
Issue: After starting a trip, the first location update is delayed by about 30 [seconds.]
The "gap" you're talking about is expressed in this screenshot from the API docs Config.stationaryRadius (see the green polylines). This is the 200 meters of movement before the plugin automatically transitions to the moving state. All of this is normal and expected behaviour. As mentioned above, if you want to manually initiate tracking immediately, call the method .changePace(true).
In the Demo App, the green play / pause button in the bottom toolbar executes .changePace(true) / .changePace(false) (like a music player).
This issue is stale because it has been open for 30 days with no activity.
This issue was closed because it has been inactive for 14 days since being marked as stale.