jordond / compass Goto Github PK
View Code? Open in Web Editor NEW🧭 Kotlin Multiplatform library location toolkit for geocoding and geolocation
Home Page: https://compass.jordond.dev
License: MIT License
🧭 Kotlin Multiplatform library location toolkit for geocoding and geolocation
Home Page: https://compass.jordond.dev
License: MIT License
The most minor issue you'll ever have, but the badges in your readme don't show the correct version. Kotlin is stating 1.9.23 while you are using 2.0.20. Compose Multiplatform states 1.6.1 while you are using 1.6.11. This makes your project look less well maintained than it actually is.
These versions of dependencies are automatically updated via a bot, but these bots don't update the readme.
So either change the bots that they update the readme as well, or just don't show version numbers in your badges (so go for https://img.shields.io/badge/kotlin-blue.svg?logo=kotlin and https://img.shields.io/badge/Compose%20Multiplatform-blue ).
The library works fine on my emulator, but on my real device, I get a GeoLocationFailed with the message: Parameter specified as non-null is null: method dev.jordond.compass.geolocation.mobile.internal.MapperKt.toModel, parameter <this>
This happens here:
override suspend fun current(priority: Priority): Location {
requirePermission(priority)
return locationManager.currentLocation(priority.toAndroidPriority).toModel()
}
locationManager.currentLocation(priority.toAndroidPriority)
is null, hence the toModel() crashing. This is because fusedLocationClient.addOnSuccessListener is triggered with null
as its value:
The big question is why this location is null
while Kotlin thinks location
is of type Location!
. This only happened after when requesting a coarse location when no other app recently retrieved a valid location. Before null
is returned, it will try to load the location for quite a long time.
The thing is, Kotlin (or more likely the annotations) is wrong. If you check out the docs, you'll see that null
can be returned in the success listener:
If unable to retrieve a current location fix before timing out, null will be returned.
This apparently happens quite often when using the coarse location (issue).
You should handle this instead of crashing and returning the crash as the message.
I could be missing something, but any reason why Geolocator.isAvailable is a suspend fun
?
It's implemented in DefaultGeolocator:
override suspend fun isAvailable(): Boolean = withContext(dispatcher) {
locator.isAvailable()
}
But locator.isAvailable()
isn't a suspend fun, so isAvailable
doesn't have to either. I think making this function not a suspend fun makes it quite a bit more usable: you can then quickly check if the GPS is turned off without having to bother with launching a coroutine.
I have created a PR, but I'm not sure if I'm missing a reason why the code works the way it does.
Currently in docs/
there are a some pages that reference the version number. The value is set to 1.0.0, with the maven central badge above it.
However the version.sh script should be modified to also update any pages in docs/
that contains the version number.
Hello! I'm seeing a crash in the demo app, when I toggle off the "Precise Location" settings.
Steps to reproduce:
Actual Result: App crashes
Expected Result: App does not crash
Error Message:
Uncaught Kotlin exception: kotlin.IllegalStateException: Already resumed
at 0 demo.debug.dylib 0x108d6614f kfun:kotlin.Throwable#<init>(kotlin.String?){} + 119
at 1 demo.debug.dylib 0x108d5f7c7 kfun:kotlin.Exception#<init>(kotlin.String?){} + 115
at 2 demo.debug.dylib 0x108d5f9e7 kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 115
at 3 demo.debug.dylib 0x108d5ff87 kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 115
at 4 demo.debug.dylib 0x108d6d57f kfun:kotlin.coroutines.SafeContinuation#resumeWith(kotlin.Result<1:0>){} + 739
at 5 demo.debug.dylib 0x108e95e37 kfun:kotlin.coroutines.Continuation#resumeWith(kotlin.Result<1:0>){}-trampoline + 99
at 6 demo.debug.dylib 0x10825d777 kfun:dev.jordond.compass.permissions.IosLocationPermissionController.requirePermissionFor$lambda$1#internal + 351
at 7 demo.debug.dylib 0x10825d98b kfun:dev.jordond.compass.permissions.IosLocationPermissionController.$requirePermissionFor$lambda$1$FUNCTION_REFERENCE$1.invoke#internal + 87
at 8 demo.debug.dylib 0x10825da8f kfun:dev.jordond.compass.permissions.IosLocationPermissionController.$requirePermissionFor$lambda$1$FUNCTION_REFERENCE$1.$<bridge-UNNB>invoke(kotlin.Int){}#internal + 123
at 9 demo.debug.dylib 0x108e9126b kfun:kotlin.Function1#invoke(1:0){}1:1-trampoline + 107
at 10 demo.debug.dylib 0x10825e943 kfun:dev.jordond.compass.permissions.mobile.internal.LocationPermissionManagerDelegate#objc:locationManagerDidChangeAuthorization: + 363
at 11 demo.debug.dylib 0x10825ec13 _636f6d706173733a636f6d706173732d7065726d697373696f6e732d6d6f62696c652f55736572732f6e6174616c69652f446f63756d656e74732f4769744875622f636f6d706173732f636f6d706173732d7065726d697373696f6e732d6d6f62696c652f7372632f696f734d61696e2f6b6f746c696e2f6465762f6a6f72646f6e642f636f6d706173732f7065726d697373696f6e732f6d6f62696c652f696e7465726e616c2f4c6f636174696f6e5065726d697373696f6e4d616e6167657244656c65676174652e6b74_knbridge14 + 183
at 12 CoreLocation 0x1844b3633 CLClientStopVehicleHeadingUpdates + 103707
at 13 CoreLocation 0x1844b2e47 CLClientStopVehicleHeadingUpdates + 101679
at 14 CoreLocation 0x1844927ff CLClientCreateWithBundleIdentifierAndPathWithWebsiteOnSilo + 6135
at 15 LocationSupport 0x18baa341b _ZN18CLConnectionServer13handleMessageEP12CLConnectionNSt3__110shared_ptrI19CLConnectionMessageEE + 18399
at 16 CoreFoundation 0x1804108e3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 19
at 17 CoreFoundation 0x180410027 __CFRunLoopDoBlocks + 351
at 18 CoreFoundation 0x18040aeef __CFRunLoopRun + 2379
at 19 CoreFoundation 0x18040a173 CFRunLoopRunSpecific + 571
at 20 GraphicsServices 0x19050cb0f GSEventRunModal + 159
at 21 UIKitCore 0x185a0287b -[UIApplication _run] + 867
at 22 UIKitCore 0x185a06467 UIApplicationMain + 123
at 23 SwiftUI 0x1c31edff3 $s7SwiftUI17KitRendererCommon33_ACC2C5639A7D76F611E170E831FCA491LLys5NeverOyXlXpFAESpySpys4Int8VGSgGXEfU_ + 163
at 24 SwiftUI 0x1c31ede9b $s7SwiftUI6runAppys5NeverOxAA0D0RzlF + 83
at 25 SwiftUI 0x1c2f63f5b $s7SwiftUI3AppPAAE4mainyyFZ + 147
at 26 demo.debug.dylib 0x10821d86f $s4demo6iOSAppV5$mainyyFZ + 39
at 27 demo.debug.dylib 0x10821d91f __debug_main_executable_dylib_entry_point + 11 (/Users/natalie/Documents/GitHub/compass/demo/iosApp/iosApp/iOSApp.swift:4:8)
at 28 dyld 0x102fc140f 0x0 + 4345041935
at 29 ??? 0x10293a0df 0x0 + 4338196703
at 30 ??? 0x35f7fffffffffff 0x0 + 243053642389651455
It's unclear what PermissionError does. The docs say:
Geocoding operation failed because of a permission error.
Either the user denied the permission, or the permission was denied forever.
But that's already what PermissionDenied
is for. It even has a forever
boolean.
So let's look at the code. It's caused by either one of these exceptions:
PermissionMissingException
PermissionRequestException
ActivityProvider
. Seems like very much an unexpected error.I would delete PermissionMissingException
, PermissionRequestException
and the entire PermissionError
. In order to do so, I would throw an IllegalStateException
in ActivityProvider
instead. This makes your library quite a bit easier: right now it's quite confusing to see the difference between PermissionError
and PermissionDenied
.
[Android] When using any of the Priority enums besides HighAccuracy such as Balanced does not update the location flow. I'm not sure if this is a Google fused location provider issue but when the user choses the 'Approximate Location' from the permission dialog no location updates are emitted from trackingStatus: Flow.
Funny story about my PR you merged which was released in 1.2.1..
Yeah, that one now causes compass to crash, sorry. See the attached PR: I think that should fix it.
A semi-related thing: this is the only place where NotFoundException() is thrown, so this is the only place where GeocoderResult.NotFound is returned. Seems odd: you'd expect this to be thrown at the other platforms as well. Create a new issue for that?
The current implementation of the library mandates that the user is prompted for "Always" authorization. It'd be great if we could have more control from the library in selecting what level of permissions we want to prompt the user with. We don't have a strong need to always be tracking the user, and would like to track them only when they're using the app.
Hey there, awesome library!
Do you guys know, how to keep receiving location updates in kmp, while the app is running in the background?
Best regards, and thank you so much for your amazing work!
It's great that we can implement our own API, but I think you should also support OpenCage as an very popular provider out of the box.
I'm trying to use it on IOs but it doesn't work
Link do github
https://github.com/kenjimaeda54/your_interests_kMP/blob/develop/iosAppYourInterests/iosAppYourInterests/Info.plist
After adding these dependency to my project
libs.version.toml :
compass = "0.1.8"
compass-geocoder = { module = "dev.jordond.compass:compass-geocoder", version.ref = "compass" }
compass-geocoder-mobile = { module = "dev.jordond.compass:compass-geocoder-mobile", version.ref = "compass" }
compass-geocoder-web-googlemaps = { module = "dev.jordond.compass:compass-geocoder-googlemaps", version.ref = "compass" }
build.gradle.kts :
val commonMain by getting {
dependencies {
api(libs.decompose.router)
implementation(libs.compass.geocoder)
implementation(libs.compass.geocoder.web.googlemaps)
..
val mobileMain by creating {
dependsOn(commonMain)
androidMain.dependsOn(this)
iosMain.dependsOn(this)
dependencies {
implementation(libs.compass.geocoder.mobile)
}
}
build project was failed with this log report
Execution failed for task ':composeApp:transformIosMainCInteropDependenciesMetadataForIde'.
Could not resolve all files for configuration ':composeApp:iosMainResolvableDependenciesMetadata'.
Could not find dev.jordond.compass:compass-geocoder-googlemaps:0.1.8.
Required by:
project :composeApp
perhaps anybody know the cause or fix for this issue
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
Warning
Renovate failed to look up the following dependencies: Failed to look up maven package convention.multiplatform:convention.multiplatform.gradle.plugin
.
Files affected: gradle/libs.versions.toml
These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.
io.ktor:ktor-serialization-kotlinx-json
, io.ktor:ktor-client-logging
, io.ktor:ktor-client-content-negotiation
, io.ktor:ktor-client-js
, io.ktor:ktor-client-core
)org.jetbrains.compose
, org.jetbrains.compose:compose-gradle-plugin
).github/workflows/ci.yml
actions/checkout v4
actions/setup-java v4
gradle/actions v4
gradle/actions v4
actions/checkout v4
actions/setup-java v4
actions/cache v4
gradle/actions v4
gradle/actions v4
actions/checkout v4
actions/setup-java v4
gradle/actions v4
gradle/actions v4
browser-actions/setup-chrome v1
.github/workflows/publish.yml
actions/checkout v4
actions/setup-java v4
gradle/actions v4
gradle/actions v4
actions/checkout v4
actions/setup-java v4
gradle/actions v4
gradle/actions v4
JamesIves/github-pages-deploy-action v4
gradle.properties
settings.gradle.kts
com.gradle.develocity 3.18
build.gradle.kts
buildLogic/gradle.properties
buildLogic/settings.gradle.kts
buildLogic/convention/build.gradle.kts
compass-autocomplete/build.gradle.kts
compass-autocomplete-geocoder-googlemaps/build.gradle.kts
compass-autocomplete-geocoder-mapbox/build.gradle.kts
compass-autocomplete-mobile/build.gradle.kts
compass-autocomplete-web/build.gradle.kts
compass-core/build.gradle.kts
compass-geocoder/build.gradle.kts
compass-geocoder-mobile/build.gradle.kts
compass-geocoder-web/build.gradle.kts
compass-geocoder-web-googlemaps/build.gradle.kts
compass-geocoder-web-mapbox/build.gradle.kts
compass-geocoder-web-opencage/build.gradle.kts
compass-geocoder-web-template/build.gradle.kts
compass-geolocation/build.gradle.kts
compass-geolocation-browser/build.gradle.kts
compass-geolocation-mobile/build.gradle.kts
compass-permissions/build.gradle.kts
compass-permissions-mobile/build.gradle.kts
compass-tools-android/build.gradle.kts
compass-tools-web/build.gradle.kts
demo/composeApp/build.gradle.kts
gradle/libs.versions.toml
androidx.activity:activity-compose 1.9.1
androidx.activity:activity-ktx 1.9.1
androidx.fragment:fragment-ktx 1.8.2
androidx.startup:startup-runtime 1.1.1
org.jetbrains.kotlinx:atomicfu 0.25.0
org.jetbrains.kotlinx:kotlinx-serialization-json 1.7.2
io.ktor:ktor-client-core 3.0.0-wasm2
io.ktor:ktor-client-okhttp 3.0.0-wasm2
io.ktor:ktor-client-android 3.0.0-wasm2
io.ktor:ktor-client-darwin 3.0.0-wasm2
io.ktor:ktor-client-js 3.0.0-wasm2
io.ktor:ktor-client-content-negotiation 3.0.0-wasm2
io.ktor:ktor-client-logging 3.0.0-wasm2
io.ktor:ktor-serialization-kotlinx-json 3.0.0-wasm2
androidx.compose.ui:ui-tooling 1.6.8
androidx.compose.ui:ui-tooling-preview 1.6.8
io.kotest:kotest-assertions-core 5.9.1
co.touchlab:kermit 2.0.4
com.google.android.gms:play-services-location 21.3.0
dev.stateholder:core 1.2.0
dev.stateholder:extensions-compose 1.2.0
dev.stateholder:extensions-voyager 1.2.0
cafe.adriel.voyager:voyager-navigator 1.1.0-beta02
cafe.adriel.voyager:voyager-screenmodel 1.1.0-beta02
com.android.tools.build:gradle 8.6.0
org.jetbrains.kotlin:kotlin-gradle-plugin 2.0.20
org.jetbrains.compose:compose-gradle-plugin 1.6.11
com.vanniktech:gradle-maven-publish-plugin 0.29.0
org.jetbrains.kotlin.multiplatform 2.0.20
org.jetbrains.compose 1.6.11
org.jetbrains.kotlin.plugin.compose 2.0.20
com.android.library 8.6.0
com.android.application 8.6.0
org.jetbrains.kotlin.android 2.0.20
org.jetbrains.kotlin.plugin.serialization 2.0.20
com.github.ben-manes.versions 0.51.0
org.jetbrains.kotlinx.binary-compatibility-validator 0.16.3
org.jetbrains.dokka 1.9.20
com.vanniktech.maven.publish 0.29.0
dev.drewhamilton.poko 0.17.0
convention.multiplatform 0
gradle/wrapper/gradle-wrapper.properties
gradle 8.10
First of all, thanks for making such an amazing library, I've been trying it out and it works very well. :)
One issue that I've noticed is that the location permission dialog is not shown when I launch the app on iOS. On Android it works fine. Maybe I'm missing some additional steps? I've included those two location strings in my plist.info file btw.
I'm also testing the app on my real iOS device (iPhone 15 Pro Max).
Here's my code:
@Composable
fun LocationTracking() {
val scope = rememberCoroutineScope()
val geoLocator = remember { Geolocator.mobile() }
val trackingStatus by geoLocator.trackingStatus
.collectAsState(initial = null)
var currentLocation: Coordinates? by remember {
mutableStateOf(null)
}
var currentCity: String? by remember {
mutableStateOf(null)
}
LaunchedEffect(Unit) {
geoLocator.trackingStatus.collectLatest { status ->
when (status) {
is TrackingStatus.Idle -> {}
is TrackingStatus.Tracking -> {}
is TrackingStatus.Update -> {}
is TrackingStatus.Error -> {
val error: GeolocatorResult.Error = status.cause
println("TRACKING ERROR: $error")
// Show the permissions settings screen
val permissionDeniedForever = error.isPermissionDeniedForever()
println("TRACKING PERMISSION DENIED: $permissionDeniedForever")
}
}
}
}
LaunchedEffect(Unit) {
geoLocator.locationUpdates.collectLatest {
currentLocation = it.coordinates
currentCity = MobileGeocoder().placeOrNull(it.coordinates)?.locality
}
}
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
text = currentCity ?: "Waiting..."
)
Spacer(modifier = Modifier.height(12.dp))
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
text = "LAT: ${currentLocation?.latitude}\nLNG: ${currentLocation?.longitude}"
)
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Button(
enabled = trackingStatus == TrackingStatus.Idle,
onClick = {
scope.launch(Dispatchers.IO) {
geoLocator.startTracking(
LocationRequest(
priority = Priority.HighAccuracy
)
)
}
}
) {
Text(text = "Start")
}
Spacer(modifier = Modifier.width(20.dp))
Button(
enabled = trackingStatus != TrackingStatus.Idle,
onClick = {
scope.launch(Dispatchers.IO) {
geoLocator.stopTracking()
currentLocation = null
currentCity = null
}
}
) {
Text(text = "Stop")
}
}
}
And this is the log that I'm seeing:
fopen failed for data file: errno = 2 (No such file or directory)
Errors found! Invalidating cache...
fopen failed for data file: errno = 2 (No such file or directory)
Errors found! Invalidating cache...
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.