Claude
Skills
Sign in
Back

kotlin-tooling-cocoapods-spm-migration

Included with Lifetime
$97 forever

Migrate KMP projects from CocoaPods (kotlin("native.cocoapods")) to Swift Package Manager (swiftPMDependencies DSL) — replaces pod() with swiftPackage(), transforms cocoapods.* imports to swiftPMImport.*, and reconfigures the Xcode project.

General

What this skill does


# CocoaPods to SwiftPM Migration for KMP

Migrate Kotlin Multiplatform projects from `kotlin("native.cocoapods")` to `swiftPMDependencies {}` DSL.

## Requirements

- **Kotlin**: 2.4.0-Beta2 or later (first public release with `swiftPMDependencies` support, available on Maven Central)
- **Xcode**: 16.4 or 26.0+
- **iOS Deployment Target**: 16.0+ recommended

## Migration Overview

**IMPORTANT**: Keep the `cocoapods {}` block and plugin active until Phase 6. The migration adds `swiftPMDependencies {}` alongside the existing CocoaPods setup first, reconfigures Xcode, and only then removes CocoaPods.

| Phase | Action |
|-------|--------|
| 1 | Analyze existing CocoaPods configuration |
| 2 | Update Gradle configuration (repos, Kotlin version) |
| 3 | Add `swiftPMDependencies {}` alongside existing `cocoapods {}` |
| 4 | Transform Kotlin imports |
| 5 | Reconfigure iOS project and deintegrate CocoaPods |
| 6 | Remove CocoaPods plugin from Gradle |
| 7 | Verify Gradle build and Xcode project build |
| 8 | Write MIGRATION_REPORT.md |

---

## Phase 1: Pre-Migration Analysis

### 1.0 Verify the project builds

Before starting migration, identify the module to migrate and confirm it compiles successfully.

1. **Find the module that uses CocoaPods** — look for `build.gradle.kts` files containing `cocoapods`:
   ```bash
   grep -rl "cocoapods" --include="build.gradle.kts" .
   ```
   Extract the module name from the path (e.g., `./shared/build.gradle.kts` → module name is `shared`). Note: multiple modules may use CocoaPods — record all of them. Typically only the module that produces the framework linked into the iOS app needs `swiftPMDependencies`; the others only need CocoaPods removed (Phase 6).

2. **Compile Kotlin code** — run the Kotlin compilation task for that module to verify the Kotlin source compiles:
   ```bash
   ./gradlew :moduleName:compileKotlinIosSimulatorArm64
   ```
   Replace `moduleName` with the directory name of the module (e.g., `:shared:compileKotlinIosSimulatorArm64`). This is faster than a full `build` (which also runs release linkage) and sufficient to verify Kotlin code correctness.

3. **Build the iOS app (optional)** — try to locate the Xcode project and build it to confirm the full app compiles:
   ```bash
   # Find the Xcode project
   find . -name "*.xcworkspace" -not -path "*/Pods/*" -maxdepth 2
   # Build (replace scheme name with the actual app scheme)
   cd /path/to/iosApp
   xcodebuild -workspace *.xcworkspace -scheme "<AppScheme>" -destination 'generic/platform=iOS Simulator' ARCHS=arm64
   ```
   If the user wants to skip the Xcode build or no Xcode project is found, proceed without it — the Kotlin compilation from step 2 is sufficient to continue.

4. **If the Kotlin compilation fails**, ask the user to either:
   - Provide the correct Gradle command to verify the module builds, or
   - Confirm the module is in a working state and it's safe to proceed

   If the user confirms without providing a build command, **record that the pre-migration build could not be verified** and warn about this at the end of migration (Phase 7).

### 1.0a Confirm Kotlin version with Swift Import support

Read the current Kotlin version from `gradle/libs.versions.toml` (or `build.gradle.kts`).

**If the project already uses Kotlin 2.4.0-Beta2 or later** → record the version and skip Phase 2.1 (no version change needed).

**If the project uses an older Kotlin version** → Phase 2.1 will upgrade it to `2.4.0-Beta2` (the first public release with `swiftPMDependencies` support, available on Maven Central — no custom repository needed). Warn the user: "⚠️ Kotlin version jump — upgrading across minor versions can introduce breaking changes unrelated to this migration. Recommended: update first, verify it builds, then re-run this migration." If the user confirms, proceed.

### 1.1 Check for deprecated CocoaPods workaround property

Search `gradle.properties` for the deprecated property:

```properties
kotlin.apple.deprecated.allowUsingEmbedAndSignWithCocoaPodsDependencies=true
```

This property was a workaround (see [KT-64096](https://youtrack.jetbrains.com/issue/KT-64096)) for projects using `embedAndSign` alongside CocoaPods dependencies. It suppresses an error about unsupported configurations that can cause runtime crashes or symbol duplication. After migrating to SwiftPM import, this property is no longer needed and **must be removed** in Phase 6. Record its presence if found.

### 1.2 Check for EmbedAndSign disablers

Search all `build.gradle.kts` files for code that disables `EmbedAndSign` tasks (e.g., `TaskGraph.whenReady` filters, `tasks.matching` blocks). This is a CocoaPods-era workaround that **breaks the migration** because `integrateEmbedAndSign` (needed in Phase 5) gets disabled too. Record any such code — it **must be removed** in Phase 6, and may need to be removed earlier. See [troubleshooting.md](references/troubleshooting.md) § "`integrateEmbedAndSign` Skipped" for patterns.

### 1.3 Check for third-party KMP libraries with bundled cinterop klibs

Some KMP libraries ship pre-built cinterop klibs with `cocoapods.*` package namespaces. After migration, the swiftPMDependencies cinterop generator detects these existing bindings and **skips generating new bindings** for those Clang modules to avoid duplicates. This means `cocoapods.*` imports for those modules must be **kept as-is** — they resolve to the third-party library's bundled klib, not to actual CocoaPods.

**Known libraries with bundled `cocoapods.*` klibs:**

| Library | Maven artifact | Bundled klib namespace | Classes provided |
|---------|---------------|----------------------|-----------------|
| [KMPNotifier](https://github.com/mirzemehdi/KMPNotifier) | `io.github.mirzemehdi:kmpnotifier` | `cocoapods.FirebaseMessaging` | `FIRMessaging`, `FIRMessagingAPNSTokenType`, etc. |

**How to detect:** Search Gradle dependency declarations for known libraries, then cross-reference their bundled namespaces against the `import cocoapods.*` statements found in step 4. Mark any matches — these imports will NOT be transformed in Phase 4.

If unsure whether a third-party KMP library bundles cinterop klibs, check if it has a `linkOnly = true` pod dependency in the project — this is a strong indicator that the library provides its own klib for those classes.

To inspect klib contents and verify bundled bindings, see [troubleshooting.md](references/troubleshooting.md) § "Third-Party KMP Libraries with Bundled Klibs".

**Find and record:**

1. **CocoaPods configuration** - Search for `cocoapods` in `build.gradle.kts` files
2. **Pod dependencies** - Extract pod names, versions from `cocoapods {}` blocks
3. **Framework configuration** - Record `baseName`, `isStatic`, deployment target from `cocoapods.framework {}`
4. **linkOnly pods** - Record pods declared with `linkOnly = true`. These have two common patterns:
   - **KMP wrapper libraries** (e.g., `dev.gitlive:firebase-*`): the wrapper provides Kotlin APIs, and the pod is only linked. See [common-pods-mapping.md](references/common-pods-mapping.md) for implications.
   - **Multi-module projects**: the consuming module declares `linkOnly = true` because a child module already provides cinterop bindings for that pod. In SwiftPM, the `swiftPackage()` declaration should go **only** in the child module that uses the pod directly. The consuming module must NOT redeclare the same packages — it only needs a `swiftPMDependencies {}` block without those packages (or an empty one if all pods were `linkOnly`). **Import namespace implication:** when the consuming module imports SPM classes that come from a child module's `swiftPMDependencies`, the import path uses the **child module's** group and name as the namespace (see Phase 4 Import Namespace Formula).
5. **Kotlin imports** - Find all `import cocoapods.*` statements. Cross-reference with step 1.3 to identify which imports come from bundled klibs (and must be preserved) vs. which come from direc

Related in General