From 21bfddb8d1638c13f817d4abe5248735b540ac1a Mon Sep 17 00:00:00 2001 From: Silvio Daminato Date: Thu, 19 Feb 2026 15:54:02 +0100 Subject: [PATCH 1/3] Return true on checkPermission also when location access is granted for "Always" on iOS --- .../com/swmansion/kmpmaps/core/LocationPermissionHandler.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/LocationPermissionHandler.kt b/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/LocationPermissionHandler.kt index 28556672..cc2fab9d 100644 --- a/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/LocationPermissionHandler.kt +++ b/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/LocationPermissionHandler.kt @@ -4,6 +4,7 @@ import kotlinx.cinterop.ExperimentalForeignApi import platform.CoreLocation.CLAuthorizationStatus import platform.CoreLocation.CLLocationManager import platform.CoreLocation.CLLocationManagerDelegateProtocol +import platform.CoreLocation.kCLAuthorizationStatusAuthorizedAlways import platform.CoreLocation.kCLAuthorizationStatusAuthorizedWhenInUse import platform.CoreLocation.kCLAuthorizationStatusNotDetermined import platform.darwin.NSObject @@ -33,7 +34,8 @@ internal class LocationPermissionHandler : NSObject(), CLLocationManagerDelegate * @return true if permission is granted, false otherwise */ fun checkPermission() = - locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse + locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse || + locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedAlways /** * Requests location permission from the user if not yet determined. From 5cd2eeb117ff9f5d9797aa991ef9ba2e6c96102e Mon Sep 17 00:00:00 2001 From: Silvio Daminato Date: Thu, 19 Feb 2026 15:55:38 +0100 Subject: [PATCH 2/3] Return true on checkPermission also when location access is granted for "Always" on iOS --- .../swmansion/kmpmaps/googlemaps/LocationPermissionHandler.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/LocationPermissionHandler.kt b/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/LocationPermissionHandler.kt index 6fa3846d..30acfe53 100644 --- a/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/LocationPermissionHandler.kt +++ b/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/LocationPermissionHandler.kt @@ -4,6 +4,7 @@ import kotlinx.cinterop.ExperimentalForeignApi import platform.CoreLocation.CLAuthorizationStatus import platform.CoreLocation.CLLocationManager import platform.CoreLocation.CLLocationManagerDelegateProtocol +import platform.CoreLocation.kCLAuthorizationStatusAuthorizedAlways import platform.CoreLocation.kCLAuthorizationStatusAuthorizedWhenInUse import platform.CoreLocation.kCLAuthorizationStatusNotDetermined import platform.darwin.NSObject @@ -33,7 +34,8 @@ internal class LocationPermissionHandler : NSObject(), CLLocationManagerDelegate * @return true if permission is granted, false otherwise */ fun checkPermission() = - locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse + locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse || + locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedAlways /** * Requests location permission from the user if not yet determined. From 111a33e43f092e138e2638b9a27e4de944bd1202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20G=C4=99siarz?= <126520293+arturgesiarz@users.noreply.github.com> Date: Fri, 20 Feb 2026 07:55:04 +0100 Subject: [PATCH 3/3] fix: enable onMarkerClick for GeoJSON layers on iOS and Android (#141) * android: Add onMarkerClick callback to GeoJSON markers * ios-google-maps: Add onMarkerClick callback to GeoJSON markers * ios-apple-maps: fix onMarkerClick callback when clustering is disabled * Refactor --- .../com/swmansion/kmpmaps/core/Extensions.kt | 67 +++++++++++-------- .../kotlin/com/swmansion/kmpmaps/core/Map.kt | 2 +- .../com/swmansion/kmpmaps/core/Extensions.kt | 2 - .../kmpmaps/core/MKGeoJsonRenderedLayer.kt | 35 ++-------- .../googlemaps/GeoJsonRendererManager.kt | 5 +- .../com/swmansion/kmpmaps/googlemaps/Map.kt | 2 +- 6 files changed, 47 insertions(+), 66 deletions(-) diff --git a/kmp-maps/core/src/androidMain/kotlin/com/swmansion/kmpmaps/core/Extensions.kt b/kmp-maps/core/src/androidMain/kotlin/com/swmansion/kmpmaps/core/Extensions.kt index fe696676..41b4e4c0 100644 --- a/kmp-maps/core/src/androidMain/kotlin/com/swmansion/kmpmaps/core/Extensions.kt +++ b/kmp-maps/core/src/androidMain/kotlin/com/swmansion/kmpmaps/core/Extensions.kt @@ -198,6 +198,7 @@ internal data class RenderedGeoJson( internal fun GoogleMap.renderGeoJsonLayer( layerData: GeoJsonLayer, clusterSettings: ClusterSettings, + onMarkerClick: ((Marker) -> Unit)?, ): RenderedGeoJson? { val json = runCatching { JSONObject(layerData.geoJson) } @@ -213,35 +214,24 @@ internal fun GoogleMap.renderGeoJsonLayer( if (clusterSettings.enabled) { for (feature in layer.features) { - if (feature.geometry !is GeoJsonPoint) continue - - val point = feature.geometry as GeoJsonPoint - - val title = feature.getProperty("title") - val snippet = feature.getProperty("snippet") - - val anchor = feature.parseGeoJsonAnchor() - val draggable = feature.getProperty("draggable")?.toBoolean() ?: false - val zIndex = feature.getProperty("zIndex")?.toFloatOrNull() - - val marker = - Marker( - coordinates = - Coordinates(point.coordinates.latitude, point.coordinates.longitude), - title = title, - androidMarkerOptions = - AndroidMarkerOptions( - snippet = snippet, - anchor = anchor, - draggable = draggable, - zIndex = zIndex, - ), - ) - extractedMarkers.add(marker) + if (feature.geometry is GeoJsonPoint) { + val point = feature.geometry as GeoJsonPoint + val marker = point.toMarker(feature) + + extractedMarkers.add(marker) - val hiddenStyle = GeoJsonPointStyle() - hiddenStyle.isVisible = false - feature.pointStyle = hiddenStyle + val hiddenStyle = GeoJsonPointStyle() + hiddenStyle.isVisible = false + feature.pointStyle = hiddenStyle + } + } + } else { + layer.setOnFeatureClickListener { feature -> + if (feature.geometry is GeoJsonPoint) { + val point = feature.geometry as GeoJsonPoint + val marker = point.toMarker(feature as GeoJsonFeature) + onMarkerClick?.invoke(marker) + } } } layer.addLayerToMap() @@ -249,6 +239,27 @@ internal fun GoogleMap.renderGeoJsonLayer( return RenderedGeoJson(layer, extractedMarkers) } +private fun GeoJsonPoint.toMarker(feature: GeoJsonFeature): Marker { + val title = feature.getProperty("title") + val snippet = feature.getProperty("snippet") + + val anchor = feature.parseGeoJsonAnchor() + val draggable = feature.getProperty("draggable")?.toBoolean() == true + val zIndex = feature.getProperty("zIndex")?.toFloatOrNull() + + return Marker( + coordinates = Coordinates(coordinates.latitude, coordinates.longitude), + title = title, + androidMarkerOptions = + AndroidMarkerOptions( + snippet = snippet, + anchor = anchor, + draggable = draggable, + zIndex = zIndex, + ), + ) +} + private fun GeoJsonFeature.parseGeoJsonAnchor(): GoogleMapsAnchor? { val anchorStr = getProperty("anchor") diff --git a/kmp-maps/core/src/androidMain/kotlin/com/swmansion/kmpmaps/core/Map.kt b/kmp-maps/core/src/androidMain/kotlin/com/swmansion/kmpmaps/core/Map.kt index a536b549..9e826702 100644 --- a/kmp-maps/core/src/androidMain/kotlin/com/swmansion/kmpmaps/core/Map.kt +++ b/kmp-maps/core/src/androidMain/kotlin/com/swmansion/kmpmaps/core/Map.kt @@ -130,7 +130,7 @@ public actual fun Map( androidGeoJsonLayers[index]?.removeLayerFromMap() - map.renderGeoJsonLayer(geo, clusterSettings)?.let { + map.renderGeoJsonLayer(geo, clusterSettings, onMarkerClick)?.let { androidGeoJsonLayers = androidGeoJsonLayers + (index to it.layer) geoJsonExtractedMarkers = geoJsonExtractedMarkers + (index to it.extractedMarkers) diff --git a/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/Extensions.kt b/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/Extensions.kt index 78f53933..82d23655 100644 --- a/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/Extensions.kt +++ b/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/Extensions.kt @@ -522,8 +522,6 @@ internal fun MKMapView.updateRenderedGeoJsonLayers( rendered.pointStyles.forEach { (pt, s) -> geoJsonPointStyles[pt] = s } rendered.overlays.forEach(this::addOverlay) - rendered.annotations.forEach(this::addAnnotation) - allExtractedMarkers.addAll(rendered.extractedMarkers) this.reapplyCorePolylineStyles(polylineStyles) diff --git a/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/MKGeoJsonRenderedLayer.kt b/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/MKGeoJsonRenderedLayer.kt index 6c01b15c..e7dbd44e 100644 --- a/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/MKGeoJsonRenderedLayer.kt +++ b/kmp-maps/core/src/iosMain/kotlin/com/swmansion/kmpmaps/core/MKGeoJsonRenderedLayer.kt @@ -47,7 +47,6 @@ public data class AppleMapsGeoJsonPointStyle(val visible: Boolean = true) */ public class MKGeoJsonRenderedLayer( internal val overlays: List, - internal val annotations: List, internal val extractedMarkers: List, internal val polylineStyles: Map = emptyMap(), internal val polygonStyles: Map = emptyMap(), @@ -56,7 +55,6 @@ public class MKGeoJsonRenderedLayer( @OptIn(ExperimentalForeignApi::class) public fun clear(from: MKMapView) { if (overlays.isNotEmpty()) from.removeOverlays(overlays) - if (annotations.isNotEmpty()) from.removeAnnotations(annotations) } } @@ -76,7 +74,6 @@ public fun MKMapView.renderGeoJson( val decoder = MKGeoJSONDecoder() val objects = decoder.geoJSONObjectsWithData(data, error = null) ?: return null - val annotations = mutableListOf() val overlays = mutableListOf() val extractedMarkers = mutableListOf() @@ -89,7 +86,6 @@ public fun MKMapView.renderGeoJson( obj = obj, mapView = this, overlays = overlays, - annotations = annotations, extractedMarkers = extractedMarkers, polylineStyles = polylineStyles, polygonStyles = polygonStyles, @@ -102,7 +98,6 @@ public fun MKMapView.renderGeoJson( return MKGeoJsonRenderedLayer( overlays = overlays, - annotations = annotations, extractedMarkers = extractedMarkers, polylineStyles = polylineStyles, polygonStyles = polygonStyles, @@ -117,7 +112,6 @@ public fun MKMapView.renderGeoJson( * @param obj GeoJSON object (feature, geometry, or array of geometries). * @param mapView Target map view (passed for API parity if needed). * @param overlays Destination list for overlays. - * @param annotations Destination list for annotations. * @param polylineStyles Destination map for polyline styles. * @param polygonStyles Destination map for polygon styles. * @param pointStyles Destination map for point styles. @@ -129,7 +123,6 @@ private fun collectAndAdd( obj: Any?, mapView: MKMapView, overlays: MutableList, - annotations: MutableList, extractedMarkers: MutableList, polylineStyles: MutableMap, polygonStyles: MutableMap, @@ -146,7 +139,6 @@ private fun collectAndAdd( g, mapView, overlays, - annotations, extractedMarkers, polylineStyles, polygonStyles, @@ -168,29 +160,11 @@ private fun collectAndAdd( polylineStyles[obj] = buildLineStyle(defaults, featureProps) } is MKPointAnnotation -> { - if (clusterSettings.enabled) { - val coordinates = obj.coordinate.useContents { Coordinates(latitude, longitude) } - val title = featureProps?.string("title") + val coordinates = obj.coordinate.useContents { Coordinates(latitude, longitude) } + val title = featureProps?.string("title") - val marker = Marker(coordinates = coordinates, title = title) - extractedMarkers.add(marker) - } else { - val title = - featureProps?.string("title") - ?: featureProps?.string("name") - ?: defaults?.pointStyle?.pointTitle - val subtitle = - featureProps?.string("snippet") - ?: featureProps?.string("description") - ?: defaults?.pointStyle?.snippet - if (title != null) obj.setTitle(title) - if (subtitle != null) obj.setSubtitle(subtitle) - - val style = buildPointStyle(defaults, featureProps) - pointStyles[obj] = style - - annotations += obj - } + val marker = Marker(coordinates = coordinates, title = title) + extractedMarkers.add(marker) } is NSArray -> { val n = obj.count.toInt() @@ -200,7 +174,6 @@ private fun collectAndAdd( any, mapView, overlays, - annotations, extractedMarkers, polylineStyles, polygonStyles, diff --git a/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/GeoJsonRendererManager.kt b/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/GeoJsonRendererManager.kt index 3b2ab495..891ff494 100644 --- a/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/GeoJsonRendererManager.kt +++ b/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/GeoJsonRendererManager.kt @@ -9,7 +9,6 @@ import cocoapods.Google_Maps_iOS_Utils.GMULineString import cocoapods.Google_Maps_iOS_Utils.GMUPoint import cocoapods.Google_Maps_iOS_Utils.GMUPolygon import cocoapods.Google_Maps_iOS_Utils.GMUStyle -import com.swmansion.kmpmaps.core.ClusterSettings import com.swmansion.kmpmaps.core.Coordinates import com.swmansion.kmpmaps.core.GeoJsonLayer import com.swmansion.kmpmaps.core.Marker @@ -40,7 +39,7 @@ internal class GeoJsonRendererManager { renderers = emptyMap() } - fun render(layers: List, clusterSettings: ClusterSettings): List { + fun render(layers: List): List { val view = mapView ?: return emptyList() val allExtractedMarkers = mutableListOf() @@ -78,7 +77,7 @@ internal class GeoJsonRendererManager { val dict = feature.properties as? NSDictionary val geometry = feature.geometry - if (geometry is GMUPoint && clusterSettings.enabled) { + if (geometry is GMUPoint) { val coordinates = geometry.coordinate.useContents { Coordinates(latitude, longitude) } val title = getString(dict, "title") diff --git a/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/Map.kt b/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/Map.kt index 56eb555b..1a236f1a 100644 --- a/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/Map.kt +++ b/kmp-maps/google-maps/src/iosMain/kotlin/com/swmansion/kmpmaps/googlemaps/Map.kt @@ -145,7 +145,7 @@ public actual fun Map( LaunchedEffect(mapView, geoJsonLayers) { if (mapView == null) return@LaunchedEffect - geoJsonExtractedMarkers = geoJsonManager.render(geoJsonLayers, clusterSettings) + geoJsonExtractedMarkers = geoJsonManager.render(geoJsonLayers) } LaunchedEffect(cameraPosition) {