
Lightweight geofencing library offering lambda/callback-based enter/exit events, permission status checks, dynamic add/remove of circular geofences, background-capable event listeners, easy integration.
A Kotlin Multiplatform library for geofencing on Android and iOS with callback-based event handling.
In your build.gradle.kts:
commonMain.dependencies {
implementation("io.github.mallikarjunpatelsh:geofence:1.0.2")
}mavenCentral() is already included by default so no extra repository setup needed.
<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" />
<application
android:name=".MyApplication">
<receiver
android:name="com.kmp.geofence.GeofenceBroadcastReceiver"
android:enabled="true"
android:exported="false" />
</application>Initialize GeofenceContext and set the event listener in your Application class. Setting the listener here ensures callbacks work even in the background:
import android.app.Application
import com.kmp.geofence.GeofenceBroadcastReceiver
import com.kmp.geofence.GeofenceContext
import com.kmp.geofence.TransitionType
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Initialize context
GeofenceContext.init(this)
// Set listener here — Application is always alive
GeofenceBroadcastReceiver.setEventListener { event ->
when (event.transitionType) {
TransitionType.ENTER -> {
println("✅ ENTERED: ${event.geofenceId}")
// handle enter — call API, save to DB, send notification, etc.
}
TransitionType.EXIT -> {
println("✅ EXITED: ${event.geofenceId}")
// handle exit
}
}
}
}
}No additional initialization needed.
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Give the description</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Give the description</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>val geofenceManager = createGeofenceManager()geofenceManager.setGeofenceEventListener(object : GeofenceEventListener {
override fun onGeofenceEnter(event: GeofenceEvent) {
println("✅ ENTERED: ${event.geofenceId}")
println("Location: ${event.latitude}, ${event.longitude}")
}
override fun onGeofenceExit(event: GeofenceEvent) {
println("✅ EXITED: ${event.geofenceId}")
println("Location: ${event.latitude}, ${event.longitude}")
}
})when (val status = geofenceManager.checkLocationPermissions()) {
is PermissionStatus.Granted -> {
println("All permissions granted")
}
is PermissionStatus.Denied -> {
println("Missing permissions: ${status.missingPermissions}")
println("Message: ${status.message}")
// Request permissions from user
}
}geofenceManager.addGeofence(
id = "delivery_location_123",
latitude = 37.7749,
longitude = -122.4194,
radius = 100f, // meters
onSuccess = {
println("Geofence added successfully")
},
onFailure = { error ->
println("Failed to add geofence: $error")
}
)geofenceManager.removeGeofences(
ids = listOf("delivery_location_123", "delivery_location_456"),
onSuccess = {
println("Geofences removed successfully")
},
onFailure = { error ->
println("Failed to remove geofences: $error")
}
)// MyApplication.kt
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
GeofenceContext.init(this)
GeofenceBroadcastReceiver.setEventListener { event ->
when (event.transitionType) {
TransitionType.ENTER -> println("✅ ENTERED: ${event.geofenceId}")
TransitionType.EXIT -> println("✅ EXITED: ${event.geofenceId}")
}
}
}
}
// MyActivity.kt
class MyActivity : ComponentActivity() {
private val geofenceManager = createGeofenceManager()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
when (val status = geofenceManager.checkLocationPermissions()) {
is PermissionStatus.Granted -> addGeofences()
is PermissionStatus.Denied -> requestPermissions(status.missingPermissions)
}
}
private fun addGeofences() {
geofenceManager.addGeofence(
id = "delivery_1",
latitude = 37.7749,
longitude = -122.4194,
radius = 100f,
onSuccess = { println("Geofence added!") },
onFailure = { error -> println("Failed: $error") }
)
}
}val geofenceManager = createGeofenceManager()
// Step 1: Set listener BEFORE addGeofence
geofenceManager.setGeofenceEventListener(object : GeofenceEventListener {
override fun onGeofenceEnter(event: GeofenceEvent) {
println("✅ ENTERED: ${event.geofenceId}")
}
override fun onGeofenceExit(event: GeofenceEvent) {
println("✅ EXITED: ${event.geofenceId}")
}
})
// Step 2: Check permissions
when (val status = geofenceManager.checkLocationPermissions()) {
is PermissionStatus.Granted -> {
// Step 3: Add geofence
geofenceManager.addGeofence(
id = "delivery_1",
latitude = 37.7749,
longitude = -122.4194,
radius = 100f,
onSuccess = { println("Geofence added!") },
onFailure = { error -> println("Failed: $error") }
)
}
is PermissionStatus.Denied -> {
println("Missing: ${status.missingPermissions}")
// request permissions
}
}addGeofence(id: String, latitude: Double, longitude: Double, radius: Float, onSuccess: () -> Unit, onFailure: (String) -> Unit)
removeGeofences(ids: List<String>, onSuccess: () -> Unit, onFailure: (String) -> Unit)
checkLocationPermissions(): PermissionStatus
setGeofenceEventListener(listener: GeofenceEventListener?)
data class GeofenceEvent(
val geofenceId: String,
val latitude: Double,
val longitude: Double,
val transitionType: TransitionType
)sealed class PermissionStatus {
object Granted : PermissionStatus()
data class Denied(
val missingPermissions: List<PermissionType>,
val message: String
) : PermissionStatus()
}enum class PermissionType {
FINE_LOCATION,
BACKGROUND_LOCATION,
PRECISE_LOCATION
}Application class for background supportaddGeofence
This library is provided as-is for use in your projects.
Feel free to submit issues and enhancement requests!
A Kotlin Multiplatform library for geofencing on Android and iOS with callback-based event handling.
In your build.gradle.kts:
commonMain.dependencies {
implementation("io.github.mallikarjunpatelsh:geofence:1.0.2")
}mavenCentral() is already included by default so no extra repository setup needed.
<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" />
<application
android:name=".MyApplication">
<receiver
android:name="com.kmp.geofence.GeofenceBroadcastReceiver"
android:enabled="true"
android:exported="false" />
</application>Initialize GeofenceContext and set the event listener in your Application class. Setting the listener here ensures callbacks work even in the background:
import android.app.Application
import com.kmp.geofence.GeofenceBroadcastReceiver
import com.kmp.geofence.GeofenceContext
import com.kmp.geofence.TransitionType
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Initialize context
GeofenceContext.init(this)
// Set listener here — Application is always alive
GeofenceBroadcastReceiver.setEventListener { event ->
when (event.transitionType) {
TransitionType.ENTER -> {
println("✅ ENTERED: ${event.geofenceId}")
// handle enter — call API, save to DB, send notification, etc.
}
TransitionType.EXIT -> {
println("✅ EXITED: ${event.geofenceId}")
// handle exit
}
}
}
}
}No additional initialization needed.
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Give the description</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Give the description</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>val geofenceManager = createGeofenceManager()geofenceManager.setGeofenceEventListener(object : GeofenceEventListener {
override fun onGeofenceEnter(event: GeofenceEvent) {
println("✅ ENTERED: ${event.geofenceId}")
println("Location: ${event.latitude}, ${event.longitude}")
}
override fun onGeofenceExit(event: GeofenceEvent) {
println("✅ EXITED: ${event.geofenceId}")
println("Location: ${event.latitude}, ${event.longitude}")
}
})when (val status = geofenceManager.checkLocationPermissions()) {
is PermissionStatus.Granted -> {
println("All permissions granted")
}
is PermissionStatus.Denied -> {
println("Missing permissions: ${status.missingPermissions}")
println("Message: ${status.message}")
// Request permissions from user
}
}geofenceManager.addGeofence(
id = "delivery_location_123",
latitude = 37.7749,
longitude = -122.4194,
radius = 100f, // meters
onSuccess = {
println("Geofence added successfully")
},
onFailure = { error ->
println("Failed to add geofence: $error")
}
)geofenceManager.removeGeofences(
ids = listOf("delivery_location_123", "delivery_location_456"),
onSuccess = {
println("Geofences removed successfully")
},
onFailure = { error ->
println("Failed to remove geofences: $error")
}
)// MyApplication.kt
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
GeofenceContext.init(this)
GeofenceBroadcastReceiver.setEventListener { event ->
when (event.transitionType) {
TransitionType.ENTER -> println("✅ ENTERED: ${event.geofenceId}")
TransitionType.EXIT -> println("✅ EXITED: ${event.geofenceId}")
}
}
}
}
// MyActivity.kt
class MyActivity : ComponentActivity() {
private val geofenceManager = createGeofenceManager()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
when (val status = geofenceManager.checkLocationPermissions()) {
is PermissionStatus.Granted -> addGeofences()
is PermissionStatus.Denied -> requestPermissions(status.missingPermissions)
}
}
private fun addGeofences() {
geofenceManager.addGeofence(
id = "delivery_1",
latitude = 37.7749,
longitude = -122.4194,
radius = 100f,
onSuccess = { println("Geofence added!") },
onFailure = { error -> println("Failed: $error") }
)
}
}val geofenceManager = createGeofenceManager()
// Step 1: Set listener BEFORE addGeofence
geofenceManager.setGeofenceEventListener(object : GeofenceEventListener {
override fun onGeofenceEnter(event: GeofenceEvent) {
println("✅ ENTERED: ${event.geofenceId}")
}
override fun onGeofenceExit(event: GeofenceEvent) {
println("✅ EXITED: ${event.geofenceId}")
}
})
// Step 2: Check permissions
when (val status = geofenceManager.checkLocationPermissions()) {
is PermissionStatus.Granted -> {
// Step 3: Add geofence
geofenceManager.addGeofence(
id = "delivery_1",
latitude = 37.7749,
longitude = -122.4194,
radius = 100f,
onSuccess = { println("Geofence added!") },
onFailure = { error -> println("Failed: $error") }
)
}
is PermissionStatus.Denied -> {
println("Missing: ${status.missingPermissions}")
// request permissions
}
}addGeofence(id: String, latitude: Double, longitude: Double, radius: Float, onSuccess: () -> Unit, onFailure: (String) -> Unit)
removeGeofences(ids: List<String>, onSuccess: () -> Unit, onFailure: (String) -> Unit)
checkLocationPermissions(): PermissionStatus
setGeofenceEventListener(listener: GeofenceEventListener?)
data class GeofenceEvent(
val geofenceId: String,
val latitude: Double,
val longitude: Double,
val transitionType: TransitionType
)sealed class PermissionStatus {
object Granted : PermissionStatus()
data class Denied(
val missingPermissions: List<PermissionType>,
val message: String
) : PermissionStatus()
}enum class PermissionType {
FINE_LOCATION,
BACKGROUND_LOCATION,
PRECISE_LOCATION
}Application class for background supportaddGeofence
This library is provided as-is for use in your projects.
Feel free to submit issues and enhancement requests!