From 4112204a5f5acef497a45ee103ae2d6f0b387c47 Mon Sep 17 00:00:00 2001 From: Olof Hedman Date: Wed, 22 Oct 2025 14:04:41 +0200 Subject: [PATCH] AppNavigator.openMap() now works on iOS Also implemented currently unused AppNavigator.openWalkingDirections() --- .../app/klottr/platform/AppNavigator.ios.kt | 74 +++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/shared/src/iosMain/kotlin/app/klottr/platform/AppNavigator.ios.kt b/shared/src/iosMain/kotlin/app/klottr/platform/AppNavigator.ios.kt index 39f4a35..94ba876 100644 --- a/shared/src/iosMain/kotlin/app/klottr/platform/AppNavigator.ios.kt +++ b/shared/src/iosMain/kotlin/app/klottr/platform/AppNavigator.ios.kt @@ -1,7 +1,49 @@ +@file:OptIn(ExperimentalForeignApi::class) + package app.klottr.platform -import platform.Foundation.NSURL +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.useContents +import platform.Foundation.NSURL import platform.UIKit.UIApplication +import platform.Foundation.NSURLComponents +import platform.Foundation.NSURLQueryItem +import platform.darwin.dispatch_async +import platform.darwin.dispatch_get_main_queue +import platform.Foundation.* + +private fun urlEncode(query: String): String { + // Percent-encode for use in URL query + val encoded = (query as NSString) + .stringByAddingPercentEncodingWithAllowedCharacters( + NSCharacterSet.URLQueryAllowedCharacterSet() + ) + return encoded ?: query +} + + +private fun isAtLeast(major: Int, minor: Int = 0, patch: Int = 0): Boolean { + val current = NSProcessInfo.processInfo.operatingSystemVersion + return current.useContents { + when { + this.majorVersion > major -> true + this.majorVersion < major -> false + this.minorVersion > minor -> true + this.minorVersion < minor -> false + else -> this.patchVersion >= patch + } + } +} + +private fun openUrlOnMain(url: NSURL) { + dispatch_async(dispatch_get_main_queue()) { + UIApplication.sharedApplication.openURL( + url, + options = emptyMap(), + completionHandler = null + ) + } +} actual object AppNavigator { actual fun openUrl(url: String) { @@ -10,13 +52,35 @@ actual object AppNavigator { } actual fun openMap(lat: Double, lon: Double, label: String?) { // Use Apple Maps URL scheme - val encoded = label?.let { it } ?: "Place" - val url = "http://maps.apple.com/?ll=$lat,$lon&q=$encoded" - openUrl(url) + // This always opens apple maps, regardless of what navigation app you've set in system settings. + // There seems to be no way around that without implementing your own chooser. + val comps = NSURLComponents(string = "maps://") + comps.path = "/" + comps.queryItems = listOf( + NSURLQueryItem(name = "ll", value = "$lat,$lon"), + NSURLQueryItem(name = "q", value = label ?: "Place") + ) + val nsUrl = comps.URL ?: return + openUrlOnMain(nsUrl) } actual fun openWalkingDirections(lat: Double?, lon: Double?, title: String?) { - //TODO: Implement + // On iOS 18.4+ there is a "default navigation app" setting in EU countries. + // This implementation makes sure to use it if available + + // Build a destination string. You can replace this with a full address if you have one. + val destination = "$lat,$lon" + val encoded = urlEncode(destination) + + val urlString = if (isAtLeast(18, 4, 0)) { + // iOS routes this to the user's chosen default navigation app + "geo-navigation:///directions?destination=$encoded" + } else { + // Apple Maps fallback: driving directions to destination + "maps://?daddr=$encoded&dirflg=d" + } + + openUrlOnMain((NSURL(string = urlString))) } }