ध्रुव, the polestar. The unmoving guide that helps you find where you are.
A small Kotlin Multiplatform location library for Android and iOS. One-shot and continuous tracking, with the right defaults baked in.
Location code looks the same in every Android codebase: import FusedLocationProviderClient,
write a LocationCallback, deal with foreground / background lifecycle, marshal to a
Flow. On iOS it's CLLocationManager, a CLLocationManagerDelegate, lifecycle
hooks. None of it is hard, but all of it is paperwork.
Dhruva is the paperwork:
- A single
LocationTrackerinterface withgetCurrentLocation()andstartTracking(). - Android implementation uses
FusedLocationProviderClient(battery-optimized, the Google-blessed default). - iOS implementation uses
CLLocationManager, with a delegate that already does the bridging dance. - Returns sealed
LocationErrors; nothing throws across the bridge. - Decoupled from any permission library so you can pair it with whatever you use (we recommend aagya, but it isn't required).
- One-shot location with configurable timeout. Fast, seeds from the last known location while waiting.
- Continuous tracking as a
Flow<Location>. Updates respect interval and distance config. Cancel the flow to stop tracking. - Lifecycle-aware. Android tracking pauses with the activity; iOS tracking stops when the app backgrounds.
- Crash-safe. Permission errors, disabled location services, and platform errors
are returned as typed
LocationErrors. - Compose-native factory.
rememberLocationTracker()for Compose; plain interface for non-Compose code.
@Composable
fun WhereAmI() {
val tracker = rememberLocationTracker()
var label by remember { mutableStateOf("Tap to find me") }
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
label = runCatching { tracker.getCurrentLocation() }
.map { "${it.latitude}, ${it.longitude}" }
.getOrElse { e -> "Couldn't locate: ${e.message}" }
}
}) {
Text(label)
}
}Continuous tracking:
LaunchedEffect(Unit) {
tracker.startTracking(LocationConfig(updateIntervalMs = 5_000))
.collect { location ->
map.center(location)
}
}| Artifact | Purpose | Required? |
|---|---|---|
io.github.ksharma-xyz:dhruva-state |
Location, LocationConfig, LocationPriority, LocationError. |
yes |
io.github.ksharma-xyz:dhruva-data |
LocationTracker interface and Android/iOS implementations. |
yes |
io.github.ksharma-xyz:dhruva-di-koin |
Optional Koin module factory. | optional |
- Android API 28+ (Android 7.0). Android target uses
FusedLocationProviderClient, so the host app must include Google Play Services. - iOS 15.3+. Uses
CLLocationManagerdirectly, no extra dependencies.
- Geocoding (forward / reverse) as a separate
dhruva-geocodingmodule. - Background location, with explicit opt-in.
- Geofencing.
- A non-Google-Play Android backend using
LocationManagerdirectly, for FOSS distribution.
- Aagya for the permission flow. The two libraries are intentionally decoupled but share design philosophy.
See CONTRIBUTING.md. PRs welcome.
Dhruva is named for ध्रुव, the polestar in Indian astronomy, traditionally the fixed point that mariners used to find their way home.