From 4faa5dc1bb31294a0381a36e9514b7c01bd7bfb9 Mon Sep 17 00:00:00 2001 From: Sean O'Donnell Date: Thu, 4 Sep 2025 12:09:48 -0400 Subject: [PATCH] implement KMM integrations and document --- .../.github/workflows/kmmbridge-publish.yml | 24 +++ .../KMMBRIDGE_INTEGRATION.md | 158 ++++++++++++++++++ .../Package.swift | 26 +++ .../gradle/libs.versions.toml | 4 +- .../shared/build.gradle.kts | 16 ++ 5 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 kotlin-swift-interopedia-samples/.github/workflows/kmmbridge-publish.yml create mode 100644 kotlin-swift-interopedia-samples/KMMBRIDGE_INTEGRATION.md create mode 100644 kotlin-swift-interopedia-samples/Package.swift diff --git a/kotlin-swift-interopedia-samples/.github/workflows/kmmbridge-publish.yml b/kotlin-swift-interopedia-samples/.github/workflows/kmmbridge-publish.yml new file mode 100644 index 0000000..07d6789 --- /dev/null +++ b/kotlin-swift-interopedia-samples/.github/workflows/kmmbridge-publish.yml @@ -0,0 +1,24 @@ +name: KMMBridge Publish +on: + workflow_dispatch: + inputs: + versionName: + description: 'Version name for this release' + required: true + default: '1.0.0' + push: + tags: + - '*' + +jobs: + call-kmmbridge-publish: + permissions: + contents: write + packages: write + uses: touchlab/KMMBridgeGithubWorkflow/.github/workflows/faktorybuild.yml@v1.1 + with: + jvmVersion: 17 + publishTask: kmmBridgePublish + xcodeVersion: 15.2 + secrets: + gradle_params: -PversionOverride=${{ github.event.inputs.versionName || github.ref_name }} \ No newline at end of file diff --git a/kotlin-swift-interopedia-samples/KMMBRIDGE_INTEGRATION.md b/kotlin-swift-interopedia-samples/KMMBRIDGE_INTEGRATION.md new file mode 100644 index 0000000..15248c8 --- /dev/null +++ b/kotlin-swift-interopedia-samples/KMMBRIDGE_INTEGRATION.md @@ -0,0 +1,158 @@ +# KMMBridge Integration Guide + +This document outlines the steps taken to integrate KMMBridge for publishing and consuming pre-built KMP (Kotlin Multiplatform) Xcode Framework binaries. + +## Overview + +KMMBridge enables publishing Kotlin Multiplatform Mobile (KMM) Xcode Frameworks to a remote repository and consuming them via Swift Package Manager (SPM), eliminating the need for iOS developers to have Kotlin toolchain installed locally. + +## Implementation Steps + +### 1. Added KMMBridge Plugin + +**File Modified:** `gradle/libs.versions.toml` +- Added KMMBridge version: `kmmbridge = "1.2.1"` +- Added plugin definition: `kmmbridge = { id = "co.touchlab.kmmbridge", version.ref = "kmmbridge" }` + +**File Modified:** `shared/build.gradle.kts` +- Applied KMMBridge plugin: `alias(libs.plugins.kmmbridge)` + +### 2. Configured KMMBridge Settings + +**File Modified:** `shared/build.gradle.kts` +- Added KMMBridge configuration block: +```kotlin +kmmbridge { + frameworkName.set("shared") + githubReleaseArtifacts() + githubReleaseVersions() + versionPrefix.set("1.0") + + gitHubPublishVersion.set( + if (project.hasProperty("versionOverride")) { + project.property("versionOverride") as String + } else { + "1.0.${System.currentTimeMillis()}" + } + ) +} +``` + +### 3. Created GitHub Actions Workflow + +**File Created:** `.github/workflows/kmmbridge-publish.yml` +- Automated publishing workflow using KMMBridge GitHub workflow +- Supports manual triggers with custom version names +- Supports automatic triggers on git tags +- Uses Xcode 15.2 and JVM 17 + +### 4. Created Package.swift + +**File Created:** `Package.swift` +- Swift Package Manager manifest for consuming the published framework +- Configured for iOS 13+ deployment target +- References framework from GitHub releases + +## Usage Instructions + +### Publishing a New Version + +#### Option 1: Manual Workflow Dispatch +1. Go to GitHub Actions in the repository +2. Select "KMMBridge Publish" workflow +3. Click "Run workflow" +4. Enter desired version name (e.g., "1.0.0") +5. Click "Run workflow" + +#### Option 2: Git Tag Release +1. Create and push a git tag: +```bash +git tag 1.0.0 +git push origin 1.0.0 +``` + +### Consuming the Framework in iOS Projects + +#### Current Setup (Direct Framework Link) +The current iOS app uses direct framework linking via the `embedAndSignAppleFrameworkForXcode` Gradle task. + +#### Migrating to Swift Package Manager + +To migrate the iOS app to consume the framework via SPM: + +1. **Remove Direct Framework Linking** + - Open `iosApp.xcodeproj` in Xcode + - Remove the shared framework from "Frameworks, Libraries, and Embedded Content" + - Remove the build script phase that runs `embedAndSignAppleFrameworkForXcode` + - Remove framework search paths pointing to `../shared/build/xcode-frameworks/` + +2. **Add Swift Package Manager Dependency** + - In Xcode, go to File → Add Package Dependencies + - Enter repository URL: `https://github.com/udiscapp/kotlin-swift-interopedia-samples` + - Select the appropriate version/branch + - Add the `shared` package to your target + +3. **Update Package.swift (After First Release)** + - Update the `remoteKotlinUrl` in `Package.swift` to point to the actual release + - Update the `remoteKotlinChecksum` with the SHA256 checksum of the framework ZIP + +#### For New iOS Projects + +1. Add the Swift package dependency in Xcode: + - Repository URL: `https://github.com/udiscapp/kotlin-swift-interopedia-samples` + - Product: `shared` + +2. Import the framework in Swift files: +```swift +import shared +``` + +## Available Gradle Tasks + +After KMMBridge integration, the following tasks are available: + +- `./gradlew kmmBridgePublish` - Publishes the framework to configured repository +- `./gradlew shared:publishAndroidDebugPublicationToGitHubRepository` - Publishes Android artifacts +- `./gradlew shared:publishKotlinMultiplatformPublicationToGitHubRepository` - Publishes multiplatform metadata + +## Configuration Details + +### Framework Configuration +- **Framework Name:** `shared` +- **Deployment Target:** iOS 13.0+ +- **Framework Type:** Static framework +- **Architectures:** arm64 (device), x86_64 + arm64 (simulator) + +### Repository Configuration +- **Artifact Storage:** GitHub Releases +- **Version Management:** GitHub Releases +- **Repository:** `udiscapp/kotlin-swift-interopedia-samples` + +## Benefits + +1. **Simplified iOS Development:** iOS developers don't need Kotlin toolchain installed +2. **Faster Build Times:** Pre-compiled frameworks reduce build time +3. **Version Management:** Clear versioning through GitHub releases +4. **CI/CD Integration:** Automated publishing through GitHub Actions +5. **Swift Package Manager:** Standard iOS dependency management + +## Troubleshooting + +### Common Issues + +1. **Missing Checksum:** Update the checksum in `Package.swift` after each release +2. **Version Conflicts:** Ensure version numbers are unique and properly incremented +3. **Framework Not Found:** Verify the GitHub release contains the framework ZIP file + +### Verification Steps + +1. Check that GitHub release contains `shared.xcframework.zip` +2. Verify Package.swift URL points to correct release +3. Confirm checksum matches the released ZIP file + +## Next Steps + +1. Publish the first version using the GitHub Actions workflow +2. Update `Package.swift` with the actual release URL and checksum +3. Test the SPM integration in a sample iOS project +4. Migrate existing iOS app from direct framework linking to SPM dependency \ No newline at end of file diff --git a/kotlin-swift-interopedia-samples/Package.swift b/kotlin-swift-interopedia-samples/Package.swift new file mode 100644 index 0000000..7e6ea35 --- /dev/null +++ b/kotlin-swift-interopedia-samples/Package.swift @@ -0,0 +1,26 @@ +// swift-tools-version:5.3 +import PackageDescription + +let remoteKotlinUrl = "https://github.com/udiscapp/kotlin-swift-interopedia-samples/releases/download/1.0.0/shared.xcframework.zip" +let remoteKotlinChecksum = "replaceme" +let packageName = "shared" + +let package = Package( + name: packageName, + platforms: [ + .iOS(.v13) + ], + products: [ + .library( + name: packageName, + targets: [packageName] + ), + ], + targets: [ + .binaryTarget( + name: packageName, + url: remoteKotlinUrl, + checksum: remoteKotlinChecksum + ) + ] +) \ No newline at end of file diff --git a/kotlin-swift-interopedia-samples/gradle/libs.versions.toml b/kotlin-swift-interopedia-samples/gradle/libs.versions.toml index 2292599..8f58c79 100644 --- a/kotlin-swift-interopedia-samples/gradle/libs.versions.toml +++ b/kotlin-swift-interopedia-samples/gradle/libs.versions.toml @@ -17,6 +17,7 @@ coroutines = "1.7.3" junit = "4.13.2" ksp = "1.9.20-1.0.14" kmp-nativecoroutines = "1.0.0-ALPHA-21" +kmmbridge = "1.2.1" kotlin = "1.9.21" [libraries] @@ -43,4 +44,5 @@ androidLibrary = { id = "com.android.library", version.ref = "agp" } jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -kmpNativeCoroutines = { id = "com.rickclephas.kmp.nativecoroutines", version.ref = "kmp-nativecoroutines"} \ No newline at end of file +kmpNativeCoroutines = { id = "com.rickclephas.kmp.nativecoroutines", version.ref = "kmp-nativecoroutines"} +kmmbridge = { id = "co.touchlab.kmmbridge", version.ref = "kmmbridge" } \ No newline at end of file diff --git a/kotlin-swift-interopedia-samples/shared/build.gradle.kts b/kotlin-swift-interopedia-samples/shared/build.gradle.kts index f30a5f4..4d5958c 100644 --- a/kotlin-swift-interopedia-samples/shared/build.gradle.kts +++ b/kotlin-swift-interopedia-samples/shared/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.androidLibrary) alias(libs.plugins.ksp) alias(libs.plugins.kmpNativeCoroutines) + alias(libs.plugins.kmmbridge) } kotlin { @@ -53,3 +54,18 @@ android { minSdk = libs.versions.android.minSdk.get().toInt() } } + +kmmbridge { + frameworkName.set("shared") + githubReleaseArtifacts() + githubReleaseVersions() + versionPrefix.set("1.0") + + gitHubPublishVersion.set( + if (project.hasProperty("versionOverride")) { + project.property("versionOverride") as String + } else { + "1.0.${System.currentTimeMillis()}" + } + ) +}