React Native – I’m making a GPS app the place I wish to observe customers location while he’s on the app and when he minimises it (operating within the background). When he utterly turns off the app (kills/terminates the app) I need the app to cease background monitoring.
I’m utilizing appState to between foreground and background however appState doesn’t account for when the app has been terminated.
AppState all the time has one or these three values:
- energetic – The app is operating within the foreground
- background – The app is operating within the background. The person is both:
- in one other app
- on the house display
- [Android] on one other Exercise (even when it was launched by your app)
- [iOS] inactive – This can be a state that happens when transitioning between foreground & background, and during times of inactivity equivalent to getting into the multitasking view, opening the Notification Heart or within the occasion of an incoming name.
How can I account for when the app has been terminated so I capable of finish the background monitoring job?
HomeScreen.tsx
import { useEffect, useState, useRef } from 'react';
import { foregroundLocationService, LocationUpdate } from '@/providers/foregroundLocation';
import { startBackgroundLocationTracking, stopBackgroundLocationTracking } from '@/providers/backgroundLocation';
import { speedCameraManager } from '@/src/providers/speedCameraManager';
export default perform HomeScreen() {
const appState = useRef(AppState.currentState);
useEffect(() => {
requestLocationPermissions();
// Deal with app state adjustments
const subscription = AppState.addEventListener('change', handleAppStateChange);
return () => {
subscription.take away();
foregroundLocationService.stopForegroundLocationTracking();
stopBackgroundLocationTracking();
console.log('HomeScreen unmounted');
};
}, []);
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
if (
appState.present.match(/inactive|background/) &&
nextAppState === 'energetic'
) {
// App has come to foreground
await stopBackgroundLocationTracking();
await startForegroundTracking();
} else if (
appState.present === 'energetic' &&
nextAppState.match(/inactive|background/)
) {
// App has gone to background
foregroundLocationService.stopForegroundLocationTracking();
await startBackgroundLocationTracking();
} else if(appState.present.match(/inactive|background/) && nextAppState === undefined || appState.present === 'energetic' && nextAppState === undefined) {
console.log('HomeScreen unmounted');
}
appState.present = nextAppState;
};
backgroundLocation.ts
import * as Location from 'expo-location';
import * as TaskManager from 'expo-task-manager';
import { cameraAlertService } from '@/src/providers/cameraAlertService';
import * as Notifications from 'expo-notifications';
import { speedCameraManager } from '@/src/providers/speedCameraManager';
import { notificationService } from '@/src/providers/notificationService';
const BACKGROUND_LOCATION_TASK = 'background-location-task';
interface LocationUpdate {
location: Location.LocationObject;
velocity: quantity; // velocity in mph
}
// Convert m/s to mph
const convertToMph = (speedMs: quantity | null): quantity => isNaN(speedMs)) return 0;
return Math.spherical(speedMs * 2.237); // 2.237 is the conversion issue from m/s to mph
;
// Outline the background job
TaskManager.defineTask(BACKGROUND_LOCATION_TASK, async ({ information, error }) => {
if (error) {
console.error(error);
return;
}
if (information) {
const { places } = information as { places: Location.LocationObject[] };
const location = places[0];
const speedMph = convertToMph(location.coords.velocity);
console.log('Background Monitoring: Location:', location, 'Velocity:', speedMph);
// Test for close by cameras that want alerts
const alertCamera = cameraAlertService.checkForAlerts(
location,
speedMph,
speedCameraManager.getCameras()
);
console.log('Background Alert Digicam:', alertCamera);
if (alertCamera) {
// Set off native notification
await notificationService.showSpeedCameraAlert(alertCamera, speedMph);
console.log('Background Notification Proven');
}
}
});
export const startBackgroundLocationTracking = async (): Promise => {
strive {
// Test if background location is offered
const { standing: backgroundStatus } =
await Location.getBackgroundPermissionsAsync();
if (backgroundStatus === 'granted') {
console.log('Background location permission granted, background location monitoring began');
}
if (backgroundStatus !== 'granted') {
console.log('Background location permission not granted');
return false;
}
// Begin background location updates
await Location.startLocationUpdatesAsync(BACKGROUND_LOCATION_TASK, {
accuracy: Location.Accuracy.Excessive,
timeInterval: 2000, // Replace each 2 seconds
distanceInterval: 5, // Replace each 5 meters
deferredUpdatesInterval: 5000, // Minimal time between updates
// Android habits
foregroundService: {
notificationTitle: "RoadSpy is energetic",
notificationBody: "Monitoring for close by velocity cameras",
notificationColor: "#FF0000",
},
// iOS habits
activityType: Location.ActivityType.AutomotiveNavigation,
showsBackgroundLocationIndicator: true,
});
return true;
} catch (error) {
console.error('Error beginning background location:', error);
return false;
}
};
export const stopBackgroundLocationTracking = async (): Promise => {
strive {
const hasStarted = await TaskManager.isTaskRegisteredAsync(BACKGROUND_LOCATION_TASK);
console.log('Is background job registered:', hasStarted);
if (hasStarted) {
await Location.stopLocationUpdatesAsync(BACKGROUND_LOCATION_TASK);
console.log('Background location monitoring stopped');
}
} catch (error) {
console.error('Error stopping background location:', error);
}
};