This API provides a simple, straightforward, and robust way to do geofencing on iOS and Android.
npx cap sync
npx cap open ios #or android
Geofencing relies on the device to passively monitor the user's precise location in the background. Per Apple and Google guidelines, your app must declare that it needs this background location access and show a UI to help users understand why. On Android you must manually show this UI.
On iOS, simply declare the capabilities:
Then add a description for the following property strings:
Privacy - Location Always and When In Use Usage Description
Privacy - Location Always Usage Description
Privacy - Location When In Use Usage Description
- Create a BroadcastReceiver that extends
PerimeterReceiver
.
public class SimpleGeofenceReceiver extends PerimeterReceiver {
@Override
public void onFenceTriggered(Context context, ArrayList<JSObject> triggeredJSFences, long triggerTime, int transitionType) {
}
@Override
public void onError(Context context, int errorCode, String errorMessage) {
}
}
- Next, create an Application class that implements
PerimeterApplicationsHooks
.
public class MyCustomApp extends Application implements PerimeterApplicationHooks {
@Override
public Class<? extends PerimeterReceiver> GetGeoFenceReceiverClass() {
return SimpleGeofenceReceiever.class;
}
}
Add the following to your AndroidManifest.xml:
<uses-feature android:name="android.hardware.location.gps" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver
android:name=".SimpleGeofenceReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
async requestPerms() : Promise<void> {
this.permStatus = await Perimeter.checkPermissions();
if(this.permStatus.foreground != "granted" || this.permStatus.background != "granted") {
this.permStatus = await Perimeter.requestPermissions();
}
}
async requestPerms() : Promise<void> {
this.permStatus = await Perimeter.checkPermissions();
if(this.permStatus.foreground != "granted") {
this.permStatus = await Perimeter.requestForegroundPermissions();
}
if (this.permStatus.background != "granted") {
this.permStatus = await Perimeter.requestBackgroundPermissions();
}
}
Before you call requestBackgroundPermissions
on Android, show a dialog to help users understand why the app needs their background location. If you do not show this dialog, your app may be rejected in review. For more information, see the following developer docs.
Start by setting up a listener for a FenceEvent
:
Perimeter.addListener("FenceEvent", (fenceEvent) => {
console.log(fenceEvent.fence.payload)
});
Next, create a Fence
object covering a given region.
let extraData = "I want to visit this place someday.";
let newFence : Fence = {
name : "Taj Mahal",
uid : "123456789",
payload: extraData,
lat : 27.1751,
lng : 78.0421,
radius : 200,
monitor : TransitionType.Enter
};
The name, UID, latitude, and longitude fields are self explanatory. For the other fields:
payload
: A extra field containingString
data that you can use. It will be delivered back to you when aFenceEvent
is triggered.radius
: The radius of the circular region to be monitored in meters. Across iOS and Android the minimum is 100m.monitor
: Specify theTransitionType
that will trigger aFenceEvent
.
Finally, call addFence(newFence)
to begin monitoring the region.
Perimeter.addFence(newFence).then(() => {
this.activeFences.push(newFence);
})
.catch((e) => {
console.log(e);
});
When the user enters and/or exits the region, you'll get a FenceEvent
that looks something like this:
FenceEvent {
fences : 'A list of fences that were triggered by a given action',
time : 'Trigger time as a UNIX timestamp',
transitionType : 'The type of action that triggered this event (enter or exit)'
};
To show a push notification on a FenceEvent install the @capacitor/local-notifications
plugin.
Perimeter.addListener("FenceEvent", (event: any) => {
let fenceEvent = (event as FenceEvent)
let fenceNames = ""
for(let fence of fenceEvent.fences) {
fenceNames += fence.name + ' '
}
LocalNotifications.schedule({
notifications : [{
id: 123,
title: 'Geofencing Event',
body : `Did you ${ fenceEvent.transitionType === TransitionTypes.Enter ? 'enter' : 'exit' } ${ fenceNames.trimEnd() }?`}]})
})
Keep in mind that iOS and Android use different background processing models. iOS wakes your entire app from the background so the main process can trigger the LocalNotifications plugin. On Android however, you'll need to use a Broadcast Receiver if you want to show notifications while the app is closed. For an example, replace SimpleGeofenceReceiever
with CustomPushGeofenceReceiver
in the sample PicketFence app.
If you need to do additional background processing on iOS in Swift, add your code to the following method in the KarmPerimeter under DevelopmentPods in Xcode.
func handleFenceEvent(triggeredRegion : CLCircularRegion, eventType : Constants.Perimeter.TransitionType)
- Geofences are cleared after a reboot on Android. Perimeter will try to automatically handle this by reloading fences from a saved store.
Copyright Mark Raymond Jr., All Rights Reserved. 2024