|
| 1 | +--- |
| 2 | +name: kmp-maps-build-guide |
| 3 | +description: Comprehensive blueprint and expert guidance for porting libraries and sample sets to Kotlin Multiplatform (KMP) and Compose Multiplatform (CMP), covering build lifecycle, CocoaPods cinterop, Xcode build phase output tracking, and platform UI interoperability. |
| 4 | +--- |
| 5 | + |
| 6 | +# KMP / CMP Porting & Build Blueprint |
| 7 | + |
| 8 | +This blueprint outlines the essential patterns, build lifecycle steps, and architecture rules for porting an existing Android library or sample catalog to **Kotlin Multiplatform (KMP)** and **Compose Multiplatform (CMP)**. |
| 9 | + |
| 10 | +--- |
| 11 | + |
| 12 | +## 1. Porting Architecture & Source Set Organization |
| 13 | + |
| 14 | +When migrating an existing library or sample set to KMP: |
| 15 | + |
| 16 | +```mermaid |
| 17 | +graph TD |
| 18 | + Common[commonMain<br/>Shared API & Composables] --> Android[androidMain<br/>AndroidView Interop] |
| 19 | + Common --> iOS[iosMain / appleMain<br/>UIKit Interop / Cinterop] |
| 20 | +``` |
| 21 | + |
| 22 | +### A. Source Set Hierarchy (`build.gradle.kts`) |
| 23 | +Ensure clear separation between pure state/composables and platform-specific view wrappers: |
| 24 | +- **`commonMain`**: Shared data models, public interface definitions (expect/actual), and platform-agnostic Compose UI. |
| 25 | +- **`androidMain`**: Android-specific implementations, Google Maps Android SDK wrappers, and `AndroidView` interop. |
| 26 | +- **`iosMain` / `appleMain`**: iOS-specific implementations, Cinterop bindings, and `UIKitView` / `UIKitViewController` wrappers. |
| 27 | + |
| 28 | +### B. Cinterop & Pod Linking (`cocoapods` Block) |
| 29 | +When wrapping native iOS SDKs (e.g., Google Maps), configure the CocoaPods plugin to generate Cinterop definitions: |
| 30 | +```kotlin |
| 31 | +kotlin { |
| 32 | + iosX64() |
| 33 | + iosArm64() |
| 34 | + iosSimulatorArm64() |
| 35 | + |
| 36 | + cocoapods { |
| 37 | + summary = "Shared KMP Library" |
| 38 | + homepage = "https://github.com/..." |
| 39 | + ios.deploymentTarget = "16.0" // Match underlying SDK requirements |
| 40 | + |
| 41 | + // Link external iOS dependency |
| 42 | + pod("GoogleMaps") { |
| 43 | + version = "10.14.0" |
| 44 | + extraOpts += listOf("-compiler-option", "-fmodules") |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +--- |
| 51 | + |
| 52 | +## 2. Platform UI Interoperability |
| 53 | + |
| 54 | +### A. Embedding Native iOS Views in Compose |
| 55 | +Use `UIKitView` or `UIKitViewController` to embed native iOS map/camera controllers inside shared Compose layouts: |
| 56 | +```kotlin |
| 57 | +@Composable |
| 58 | +actual fun PlatformMapView(modifier: Modifier) { |
| 59 | + UIKitViewController( |
| 60 | + factory = { MyNativeViewController() }, |
| 61 | + modifier = modifier |
| 62 | + ) |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +### B. Exposing Compose to iOS Sample Apps |
| 67 | +Wrap root KMP composables in a `ComposeUIViewController` for seamless ingestion into native `UIViewController` or `UITableView` sample architectures: |
| 68 | +```kotlin |
| 69 | +fun MainViewController(): UIViewController = ComposeUIViewController { |
| 70 | + MySharedSampleScreen() |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +--- |
| 75 | + |
| 76 | +## 3. iOS Build Lifecycle & Xcode Integration |
| 77 | + |
| 78 | +### A. Pre-Generate Dummy Framework |
| 79 | +Before running `pod install` in your sample iOS app, you must generate the initial Kotlin framework: |
| 80 | +```bash |
| 81 | +./gradlew :<kmp_module_name>:generateDummyFramework |
| 82 | +``` |
| 83 | +*(Prevents CocoaPods from failing with `Kotlin framework doesn't exist yet`)*. |
| 84 | + |
| 85 | +### B. Script Phase Output Tracking (Xcode 16+) |
| 86 | +When adding custom **Run Script Build Phases** (e.g., dynamic secrets injection or code generation) to your Xcode targets: |
| 87 | +- **Rule**: Always declare the generated source output path (e.g., `$(SRCROOT)/iosApp/DeveloperSecrets.swift`) in the **Output Files** (`output_paths`) of that build phase. |
| 88 | +- **Why**: Modern Xcode build systems enforce strict input/output verification during dependency graph analysis and will fail with `Build input file cannot be found` if the output file is undeclared. |
| 89 | + |
| 90 | +### C. Pod Installation & Repo Sync |
| 91 | +Ensure your local podspec cache is updated when referencing new SDK versions: |
| 92 | +```bash |
| 93 | +pod install --repo-update |
| 94 | +``` |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | +## 4. Secrets & Credential Injection |
| 99 | + |
| 100 | +To keep sample repositories secure: |
| 101 | +1. Store API keys locally in an uncommitted `secrets.properties` file: |
| 102 | + ```properties |
| 103 | + MAPS_API_KEY=AIzaSy... |
| 104 | + ``` |
| 105 | +2. Inject keys via a Gradle task (for Android) or a pre-build Run Script (for iOS) that outputs a Git-ignored configuration struct (`DeveloperSecrets.swift`). |
| 106 | + |
| 107 | +--- |
| 108 | + |
| 109 | +## 5. Verification Commands |
| 110 | + |
| 111 | +- **Verify Android Library & Sample App**: |
| 112 | + ```bash |
| 113 | + ./gradlew assembleDebug |
| 114 | + ``` |
| 115 | +- **Verify iOS XCFramework Compilation**: |
| 116 | + ```bash |
| 117 | + ./gradlew :<kmp_module_name>:podPublishDebugXCFramework |
| 118 | + ``` |
0 commit comments