icerockdev / moko-maps

Control your map from common code for mobile (android & ios) Kotlin Multiplatform development
https://moko.icerock.dev/
Apache License 2.0
84 stars 16 forks source link

Will this library work with KMP and Compose for iOS? #72

Open realityexpander opened 1 year ago

sdzshn3 commented 11 months ago

Doesn't seem like

realityexpander commented 11 months ago

What is the suggested alternative?

sdzshn3 commented 11 months ago

I have done something like this

@Composable
expect fun GoogleMaps(
    modifier: Modifier,
    markers: List<MapMarker>? = null,
    cameraPosition: CameraPosition? = null,
    cameraPositionLatLongBounds: CameraPositionLatLongBounds? = null,
    polyLine: List<LatLong>? = null
)
@Composable
actual fun GoogleMaps(
    modifier: Modifier,
    markers: List<MapMarker>?,
    cameraPosition: CameraPosition?,
    cameraPositionLatLongBounds: CameraPositionLatLongBounds?,
    polyLine: List<LatLong>?
) {

    val cameraPositionState = rememberCameraPositionState()

    LaunchedEffect(cameraPosition) {
        cameraPosition?.let {
            cameraPositionState.animate(
                CameraUpdateFactory.newLatLngZoom(
                    LatLng(
                        it.latLong.latitude,
                        it.latLong.longitude
                    ), it.zoom
                )
            )
        }
    }

    LaunchedEffect(cameraPositionLatLongBounds) {
        cameraPositionLatLongBounds?.let {

            val latLngBounds = LatLngBounds.builder().apply {
                it.coordinates.forEach { latLong ->
                    include(LatLng(latLong.latitude, latLong.longitude))
                }
            }.build()

            cameraPositionState.move(
                CameraUpdateFactory.newLatLngBounds(latLngBounds, it.padding)
            )
        }
    }

    GoogleMap(
        cameraPositionState = cameraPositionState,
        modifier = modifier
    ) {
        markers?.forEach { marker ->
            Marker(
                state = rememberMarkerState(
                    key = marker.key,
                    position = LatLng(marker.position.latitude, marker.position.longitude)
                ),
                alpha = marker.alpha,
                title = marker.title
            )
        }

        polyLine?.let { polyLine ->
            Polyline(
                points = List(polyLine.size) {
                    val latLong = polyLine[it]
                    LatLng(latLong.latitude, latLong.longitude)
                },
                color = Color(0XFF1572D5),
                width = 16f
            )
            Polyline(
                points = List(polyLine.size) {
                    val latLong = polyLine[it]
                    LatLng(latLong.latitude, latLong.longitude)
                },
                color = Color(0XFF00AFFE),
                width = 8f
            )
        }

    }
}
@Composable
actual fun GoogleMaps(
    modifier: Modifier,
    markers: List<MapMarker>?,
    cameraPosition: CameraPosition?,
    cameraPositionLatLongBounds: CameraPositionLatLongBounds?,
    polyLine: List<LatLong>?
) {
    val mapsView = remember {
        GMSMapView()
    }

    UIKitView(
        modifier = modifier.fillMaxSize(),
        interactive = true,
        factory = {
            mapsView
        },
        update = { view ->
            cameraPosition?.let {
                view.setCamera(
                    GMSCameraPosition.cameraWithLatitude(
                        it.latLong.latitude,
                        it.latLong.longitude,
                        it.zoom
                    )
                )
            }

            cameraPositionLatLongBounds?.let {

                val bounds = GMSCoordinateBounds()
                it.coordinates.forEach {
                    bounds.includingCoordinate(
                        CLLocationCoordinate2DMake(
                            latitude = it.latitude,
                            longitude = it.longitude
                        )
                    )
                }
                GMSCameraUpdate().apply {
                    fitBounds(bounds, it.padding.toDouble())
                    view.animateWithCameraUpdate(this)
                }
            }

            markers?.forEach { marker ->
                GMSMarker().apply {
                    position = CLLocationCoordinate2DMake(
                        marker.position.latitude,
                        marker.position.longitude
                    )
                    title = marker.title
                    map = view
                }
            }

            polyLine?.let { polyLine ->
                val points = polyLine.map {
                    CLLocationCoordinate2DMake(it.latitude, it.longitude)
                }
                val path = GMSMutablePath().apply {
                    points.forEach { point ->
                        addCoordinate(point)
                    }
                }

                GMSPolyline().apply {
                    this.path = path
                    this.map = view
                }
            }
        }
    )
}
realityexpander commented 11 months ago

Thanks for the sample code! I can see that the code is using the native libraries on ios and android.

I'm primarily and Android native dev, so iOS is still a bit mysterious for me, especially without having example code to work from.

Is there any other special handling to use this code? Like the cocopods setup in this project? Or just a standard google setup?

Is there a more complete example of your code that I could reference for this?

Im confused about what needs to be in place on the iOS side to make this work. I can handle the Android side.

Thanks in advance!

sdzshn3 commented 11 months ago

I'm also in a stage of trail and error to make the maps work on both platforms.

As I keep adding more features, I started to have a feel like, it's better to use GoogleMaps View directly and set the view as AndroidView in Compose. This way we have a GoogleMap object which gives us full control and write better code.

And regarding integration of Maps in ios, I have used cocoapods.

in shared gradle:

kotlin {
    cocoapods {
        // ...
        pod("GoogleMaps") {
            version = "8.2.0"
        }
    }
}

After that once you do a gradle sync, you should be able to use GoogleMaps related classes in iosMain of shared module.

For API usage, refer official docs https://developers.google.com/maps/documentation/ios-sdk/config

realityexpander commented 11 months ago

I have finally got the Android and iOS displaying maps with interactivity. There are some features on iOS that are not working properly, but the basic functionality is implemented.

Check out my solution here:

https://github.com/realityexpander/ContactsComposeMultiplatform

andiosdev commented 1 month ago

I'm new in KMP and Compose, can you guys give me a beginner instruction on how to integrate Google Maps in iOS?