react-native-background-geolocation
react-native-background-geolocation copied to clipboard
[Help Wanted]: BackgroundGeolocation Keeps Sending Location Updates Even After Calling stop()
Required Reading
- [x] Confirmed
Plugin Version
^4.16.5
Mobile operating-system(s)
- [x] iOS
- [x] Android
What do you require assistance about?
We are using react-native-background-geolocation to track user location after they grant access. We are using the URL method to automatically send location updates to a server.
The issue occurs when a user is clocked out from a different device. In this case, we want to stop tracking their location immediately and stop hitting ping api.
We attempted to achieve this using the onHttp event as follows:
Observed Behavior: The API is still receiving location updates even after stop() is invoked.
[Optional] Plugin Code and/or Config
useEffect(() => {
if (Platform.OS !== 'web' && token?.authToken) {
const onProvider: Subscription = BackgroundGeolocation.onProviderChange((event) => {
switch (event.status) {
case BackgroundGeolocation.AUTHORIZATION_STATUS_DENIED:
break;
case BackgroundGeolocation.AUTHORIZATION_STATUS_ALWAYS:
break;
case BackgroundGeolocation.AUTHORIZATION_STATUS_WHEN_IN_USE:
break;
}
});
const onLocation: Subscription = BackgroundGeolocation.onLocation(
(location) => {
setLongitude(location?.coords.longitude);
setLatitude(location?.coords.latitude);
},
(error) => {
console.log('[onLocation] ERROR: ', error);
}
);
const http: Subscription = BackgroundGeolocation.onHttp((response) => {
console.log('http', response);
if (response.status !== 200) {
BackgroundGeolocation.stop();
}
});
const motionChange: Subscription = BackgroundGeolocation.onMotionChange((event) => {});
/// 2. ready the plugin.
BackgroundGeolocation.ready({
notification: {
priority: BackgroundGeolocation.NOTIFICATION_PRIORITY_DEFAULT,
title: 'abc',
text: 'We are tracking you',
},
isMoving: false,
url: `${config.API_URL}/api/ping`,
autoSync: false,
maxRecordsToPersist: 1,
method: 'POST',
elasticityMultiplier: 6,
distanceFilter: 15,
disableLocationAuthorizationAlert: !getAllowAllLocationUdf,
headers: {
Authorization: 'Bearer ' + token.authToken,
timezone: moment.tz.guess(),
},
showsBackgroundLocationIndicator: true,
httpRootProperty: '.',
locationTemplate:
'{"latitude":<%= latitude %>,"longitude":<%= longitude %>,"time":"<%= timestamp %>"}',
backgroundPermissionRationale: {
title: 'Allow abc to use the location',
message:
'abc utilizes your GPS while clocked in for seamless collaboration among all staff.',
positiveAction: 'Change to {backgroundPermissionOptionLabel}',
negativeAction: 'Cancel',
},
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
stopTimeout: 1,
debug: false,
logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE,
stopOnTerminate: false,
startOnBoot: true,
})
.then((state) => {})
.catch((err) => console.error(err));
return () => {
onProvider.remove();
onLocation.remove();
http.remove();
motionChange.remove();
};
}
}, [getAllowAllLocationUdf]);
const delayedReset = () => {
BackgroundGeolocation.reset({
notification: {
priority: BackgroundGeolocation.NOTIFICATION_PRIORITY_DEFAULT,
title: 'Lumber',
text: 'Lumber is tracking you',
},
isMoving: true,
url: `${config.API_URL}/api/ping`,
autoSync: status === 'CLOCKED IN' ? true : false,
maxRecordsToPersist: 1,
method: 'POST',
elasticityMultiplier: 6,
distanceFilter: 15,
disableLocationAuthorizationAlert: !getAllowAllLocationUdf,
headers: {
Authorization: 'Bearer ' + token.authToken,
timezone: moment.tz.guess(),
},
showsBackgroundLocationIndicator: true,
httpRootProperty: '.',
locationTemplate:
'{"latitude":<%= latitude %>,"longitude":<%= longitude %>,"time":"<%= timestamp %>"}',
backgroundPermissionRationale: {
title: 'Allow abc to use the location',
message:
'abc utilizes your GPS while clocked in for seamless collaboration among all staff.',
positiveAction: 'Change to {backgroundPermissionOptionLabel}',
negativeAction: 'Cancel',
},
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
stopTimeout: 1,
debug: false,
logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE,
stopOnTerminate: false,
startOnBoot: false,
})
.then((state) => {})
.catch((err) => console.error(err));
};
useEffect(() => {
if (Platform.OS !== 'web' && token?.authToken) {
delayedReset();
}
}, [status, token, getAllowAllLocationUdf]);
[Optional] Relevant log output
Calling .stop() stops everything. The plug-in does not continue recording locations and posting to your configured url after calling .stop()
I added a console.log inside the onHttp event to debug the issue:
const http: Subscription = BackgroundGeolocation.onHttp((response) => {
console.log('http', response);
if (response.status !== 200) {
console.log('Stopping BackgroundGeolocation...');
BackgroundGeolocation.stop();
}
});
However, when the API returns a 500 or 400 error, the if condition is never triggered. It seems like the onHttp event is not even receiving the response in such cases.
I want to confirm:
- Does the URL method automatically stop tracking when the API returns an error response (e.g., 4xx or 5xx)?
- Is there any internal mechanism that prevents onHttp from being triggered when the server returns an error?
- If onHttp is not fired for error responses, what is the recommended way to detect such failures and stop tracking?
The way to debug this is by observing the plug-in logs.
See wiki “Debugging”.
Hi Chris, Do you have any suggestions for trying a fake GPS on an iOS real device for debugging? That would be helpful.
The iOS Simulator and XCode have built-in ability to simulate location.
For real devices, you can provide a custom GPX file and load it in the Launch Scheme. I have attached a custom GPX file that follows the iOS Simulator "Freeway Drive" route (rename the file CityDrive.gpx.txt -> CityDrive.gpx)
Also google "iOS simulate location xcode": https://medium.com/@merlos/how-to-simulate-locations-in-xcode-b0f7f16e126d
This function is working fine on Android, but while debugging on iOS, I noticed that the logs from this function are not being printed when the app is in the background. As a result, the location tracking is not stopping. How can I resolve this issue?
const http: Subscription = BackgroundGeolocation.onHttp((response) => {
console.log('http', response);
if (response.status !== 200) {
console.log('Stopping BackgroundGeolocation...');
BackgroundGeolocation.stop();
}
});
This issue is stale because it has been open for 30 days with no activity.
When the app is in the background or has been killed from the foreground, we are still receiving location notifications. However, during this time, we are not tracking the user's location, so we don’t want users to feel like their location is being tracked unnecessarily. Is there a way to remove or suppress this notification in such cases?
useEffect(() => {
if (Platform.OS !== 'web' && token?.authToken) {
const onProvider: Subscription = BackgroundGeolocation.onProviderChange((event) => {
switch (event.status) {
case BackgroundGeolocation.AUTHORIZATION_STATUS_DENIED:
break;
case BackgroundGeolocation.AUTHORIZATION_STATUS_ALWAYS:
break;
case BackgroundGeolocation.AUTHORIZATION_STATUS_WHEN_IN_USE:
break;
}
});
const onLocation: Subscription = BackgroundGeolocation.onLocation(
(location) => {
setLongitude(location?.coords.longitude);
setLatitude(location?.coords.latitude);
},
(error) => {
console.log('[onLocation] ERROR: ', error);
}
);
const http: Subscription = BackgroundGeolocation.onHttp(async (response) => {
console.log('http', response);
if (response.status !== 200) {
await BackgroundGeolocation.stop();
}
});
const motionChange: Subscription = BackgroundGeolocation.onMotionChange((event) => {});
/// 2. ready the plugin.
BackgroundGeolocation.ready({
notification: {
priority: BackgroundGeolocation.NOTIFICATION_PRIORITY_DEFAULT,
title: 'abc',
text: 'abc uses your location to help ____',
},
isMoving: false,
url: `api/ping`,
autoSync: false,
maxRecordsToPersist: 1,
method: 'POST',
elasticityMultiplier: 6,
distanceFilter: 15,
disableLocationAuthorizationAlert: !getAllowAllLocationUdf,
headers: {
Authorization: 'Bearer ' + token.authToken,
timezone: moment.tz.guess(),
},
showsBackgroundLocationIndicator: true,
httpRootProperty: '.',
locationTemplate:
'{"latitude":<%= latitude %>,"longitude":<%= longitude %>,"time":"<%= timestamp %>"}',
backgroundPermissionRationale: {
title: 'Allow abc to use the location',
message:
'abc utilizes your GPS.',
positiveAction: 'Change to {backgroundPermissionOptionLabel}',
negativeAction: 'Cancel',
},
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
stopTimeout: 1,
debug: false,
logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE,
stopOnTerminate: false,
startOnBoot: true,
})
.then((state) => {})
.catch((err) => console.error(err));
return () => {
onProvider.remove();
onLocation.remove();
http.remove();
motionChange.remove();
};
}
}, []);
I suggest you search your code for instance of calling .getCurrentPosition
I'm not using .getCurrentPosition anywhere in my code. Instead, I'm using .onLocation in multiple places. Could this be the reason for the issue?
I'm using .onLocation in multiple places. Could this be the reason for the issue?
No. .onLocation is merely an event-listener.
I suggest you capture plug-in logs where this phenomenon occurs. See api docs .emailLog
We had added logs to be stored in our AWS S3 bucket using:
const uploadPreviousDayLogs = async () => {
const endOfYesterday = moment().startOf('day').subtract(1, 'seconds').toDate();
const startOfYesterday = moment(endOfYesterday).startOf('day').toDate();
const apiUrl = `${config.API_URL}/api/geolog/timestamps/${startOfYesterday.getTime()}/log/`;
try {
await BackgroundGeolocation.logger.uploadLog(apiUrl);
await BackgroundGeolocation.logger.destroyLog();
} catch (error) {
await BackgroundGeolocation.logger.destroyLog();
console.log('[uploadLog] Error:', error);
return;
}
};
useEffect(() => {
if (Platform.OS === 'android') {
uploadPreviousDayLogs();
}
}, [status]);
However, this occasionally caused the app to crash, although it was not reproducible locally and occurred very rarely. Due to this instability, we have now removed the log capturing functionality.
I just need to know one more thing: Does the Geolocation SDK store location data within the app? Because in our app, we haven’t written any code to explicitly remove location data from SQLite or any local storage.
The plug-in inserts each recorded location into its SQLite db. When your Config.url returns a 200 response, the plug-in deletes that record from SQLite.
The db is typically empty.
A record can exist in the db only for Config.maxDaysToPersist before being expunged.
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.