GeoFencing in React Native

Dipesh Koirala
readytowork, Inc.
Published in
5 min readMar 24, 2023
Source: GIS Geography

Geo-fence is a location-based technology that creates a virtual boundary around a physical location or area, such as a store, a park, or a building. It allows you to set up triggers that get activated when a device, such as a smartphone or a GPS tracker, enters or exits a defined area.

Geo-fencing technology relies on GPS, Wi-Fi, cellular data, or other location-based technologies to determine the user’s location and trigger the appropriate actions based on their location. Geo-fencing is used in various industries, including retail, transportation, and logistics, to track assets, monitor employee movements, and send targeted notifications and promotions to customers.

If you are developing an application to implement geofencing using React Native, it was very hard to find continuously maintained packages. So, in that case, I either had two options; to write a native code, or to resort to react-native-background-geolocation. This package was well maintained but, we have to buy a license from them if we are going to use it in release mode.

So, after doing a lot of digging and trying up all the unmaintained packages, I finally found the package I was looking for. So, today, we are going to learn about the way to implement geofencing in mobile applications.

Step 1: Install the required libraries to get started,

you need to install the following libraries:

  • expo-location
  • expo-task-manager

These are expo managed packages and can be used Bare React Native CLI project as well. Please follow this documentation to install expo in Bare React Native Cli project.

After Successfully installing the latest expo-module, enter the following command in the terminal

npm install expo-location expo-task-manager
OR
yarn add expo-location expo-task-manager

Step 2: Get user permission to access the location before you can start monitoring the user’s location, you need to ask for their permission. You can do this by using the expo-location library itself. To do this, you have the following permissions added toAndroidManifest.xml the file first.


<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

After adding permissions in Android manifest, use this code to request the user’s location permission. First, you have to request Foregound location permission, and then only you will be allowed to change the app to access location all the time, for background location tracking.

import * as Location from 'expo-location';

useEffect(() => {
const requestPermissions = async () => {
const foreground = await Location.requestForegroundPermissionsAsync();
if (foreground.granted)
await Location.requestBackgroundPermissionsAsync();
};
requestPermissions();
}, []);

Step 3: Setup the Geofence

Next, you need to set up the geofence by creating an Geofence object. This object contains the following properties:

  1. latitude: The latitude of the center point of the geofence.
  2. longitude: The longitude of the center point of the geofence.
  3. radius: The radius of the geofence in meters.
  4. id: A unique identifier for the geofence.
const geofence = {
latitude: 22.706141,
longitude: 100.384468,
radius: 500, // 500 meters
};

Step 4: Define two background tasks using expo-task-manager : One to track the background location and the other to track whether the subject has entered a predefined geofence or not.

import * as TaskManager from 'expo-task-manager';
import * as Location from 'expo-location';

const BACKGROUND_LOCATION = 'BACKGROUND_LOCATION';
const GEOFENCE_BOUNDARY = 'GEOFENCE_BOUNDARY';

//Task one, to track geofence boundary

TaskManager.defineTask(
GEOFENCE_BOUNDARY,
({data: {eventType, region}, error}) => {
if (error) {
console.log(error.message, 'geofence error');
return;
}
if (eventType === Location.GeofencingEventType.Enter) {
console.log("You've entered region:", region);
} else if (eventType === Location.GeofencingEventType.Exit) {
console.log("You've left region:", region);
}
},
);

//Task two, to track background location
TaskManager.defineTask(BACKGROUND_LOCATION, async ({data, error}) => {
if (error) {
console.error(error.message, 'background location error');
return;
}
if (data) {
const {locations} = data;
const location = locations[0];
if (location) {
console.log('Location in background', location.coords);

}
}
});

Step 5: Listen to the background location updates by calling the function below on button press:

 const startBackgroundUpdate = async () => {
// Don't track position if permission is not granted
const {granted} = await Location.getBackgroundPermissionsAsync();
if (!granted) {
console.log('location tracking denied');
return;
}

// Make sure the task is defined otherwise do not start tracking
const isTaskDefined = TaskManager.isTaskDefined(BACKGROUND_LOCATION);
if (!isTaskDefined) {
console.log('Task is not defined');
return;
}

// Don't track if it is already running in background
const hasStarted = await Location.hasStartedLocationUpdatesAsync(
BACKGROUND_LOCATION,
);
if (hasStarted) {
console.log('Already started');
return;
}

await Location.startLocationUpdatesAsync(BACKGROUND_LOCATION, {
accuracy: Location.Accuracy.BestForNavigation,
timeInterval: 20000,// in milliseconds: track location in every 20 seconds
showsBackgroundLocationIndicator: true,
foregroundService: {
notificationTitle: 'Location',
notificationBody: 'Location tracking in background',
notificationColor: '#fff',
},
});
};

Step 6: Call the geofence task, whenever a location gets updated by updating the code of task two of step 4:

TaskManager.defineTask(BACKGROUND_LOCATION, async ({data, error}) => {
if (error) {
console.error(error.message, 'background location error');
return;
}
if (data) {
const {locations} = data;
const location = locations[0];
if (location) {
console.log('Location in background', location.coords);

// updated code start

const isTaskDefined = TaskManager.isTaskDefined(GEOFENCE_BOUNDARY);
if (!isTaskDefined) {
console.log('Task is not defined');
return;
}
await Location.startGeofencingAsync(GEOFENCE_BOUNDARY, [
{
identifier: 'my-geofence',
latitude: geofence.latitude,
longitude: geofence.longitude,
radius: geofence.radius,
notifyOnEnter: true,
notifyOnExit: true,
},
]);
// updated code end
}
}
});

So, what this task does is it triggers a location update every 20 seconds. If the location coordinates are available, then it triggers geofence on either entering or exiting the geofence boundary shown in the code below.

import * as TaskManager from 'expo-task-manager';
import * as Location from 'expo-location';

const GEOFENCE_BOUNDARY = 'GEOFENCE_BOUNDARY';

//Task one, to track geofence boundary

TaskManager.defineTask(
GEOFENCE_BOUNDARY,
({data: {eventType, region}, error}) => {
if (error) {
console.log(error.message, 'geofence error');
return;
}
if (eventType === Location.GeofencingEventType.Enter) {
console.log("You've entered region:", region);
} else if (eventType === Location.GeofencingEventType.Exit) {
console.log("You've left region:", region);
}
},
);

Conclusion

In this article, we have seen how to implement geo-fencing in an Android application using React Native. By following these simple steps, you can easily monitor the user’s location and trigger actions when they enter or exit a predefined boundary. I hope this helps to implement Geofencing in Android.

Here is the link to the GitHub repo. Please feel free to ask me if you have any queries regarding this.

Happy Coding🎉🎉

--

--