Skip to content

Geofencing

Beekon can watch circular regions and tell you when the device crosses their boundary. Geofences are independent of tracking — they keep firing across start() / stop() cycles, survive process death, and survive reboot. You don’t have to be actively tracking for a crossing to arrive.

Under the hood it’s the platform’s own geofencing engine — GMS GeofencingClient on Android (auto-paged, so you can register more than the OS per-app limit) and CLMonitor on iOS. The crossing vocabulary is Enter / Exit only; Android’s DWELL transition is not surfaced.

These are your geofences — self-managed mode. In Beekon Cloud the server owns the set, and addGeofences reports GeofencesManagedByServer instead.

A geofence is a circle plus two notify flags:

FieldTypeRangeDefault
idstring1 … 100 chars; re-adding a matching id replaces it
latitudenumber (°)−90 … +90, WGS-84
longitudenumber (°)−180 … +180, WGS-84
radiusMetersnumber (m)> 0; ≥ 100 recommended for reliable triggering
notifyOnEntrybooltrue
notifyOnExitbooltrue

The OS geofencing backends are reliable from roughly 100 m up — a tighter radius triggers late or not at all, so prefer ≥ 100 even though anything > 0 is accepted.

Geofence management is add / remove / list — there’s no setGeofences.

MethodBehaviour
addGeofences(list)Register geofences. Re-adding an existing id replaces it. Atomic — if any entry fails validation, none are added.
removeGeofences(ids)Unregister by id. Unknown ids are ignored.
listGeofences()The currently registered set.
Beekon.addGeofences(listOf(
BeekonGeofence(id = "home", latitude = 12.9716, longitude = 77.5946, radiusMeters = 150.0),
))
val current: List<BeekonGeofence> = Beekon.listGeofences()
Beekon.removeGeofences(listOf("home"))
// addGeofences throws BeekonException.InvalidGeofence on a bad entry

Crossings arrive on the geofenceEvents stream as a GeofenceEvent:

FieldMeaning
idUnique event id (SDK-generated) — the sync dedup key.
geofenceIdThe BeekonGeofence.id that was crossed.
typeEnter or Exit.
timestampWhen the crossing happened (UTC).
Beekon.geofenceEvents.collect { e ->
Log.d("beekon", "${e.type} ${e.geofenceId} @ ${e.timestamp}")
}

Registered geofences are persisted by the native SDK, so they outlive the process. A crossing can wake a terminated app and deliver an event before any tracking session exists. This is why geofencing is documented separately from the lifecycle state machine: Beekon.state describes the tracking session, while geofences run on the OS’s own monitoring budget regardless of that state.