AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified Apache-2.0 Self-run

Kotlin Tooling Cocoapods Spm Migration

skill-kotlin-kotlin-agent-skills-kotlin-tooling-cocoapods-spm-migration · by Kotlin

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.

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add skill-kotlin-kotlin-agent-skills-kotlin-tooling-cocoapods-spm-migration

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-kotlin-kotlin-agent-skills-kotlin-tooling-cocoapods-spm-migration)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Kotlin Tooling Cocoapods Spm Migration? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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).

  1. 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.

  1. 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 "" -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.

  1. 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:

kotlin.apple.deprecated.allowUsingEmbedAndSignWithCocoaPodsDependencies=true

This property was a workaround (see 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 | 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).
  1. 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 direct pod cinterop (and must be transformed).
  2. Map pods to SPM - See [common-pods-mapping.md](references/common-pods-mapping.md)
  3. Locate iOS project directory - Find the directory containing Podfile and .xcworkspace:

``bash find . -name "Podfile" -type f ` Record this path (e.g., iosApp/, ios/`, or project root) - needed for Phase 5

  1. Check for non-KMP CocoaPods - Determine if the project uses CocoaPods for dependencies other than KMP. This affects cleanup strategy in Phase 5.
  2. Cross-reference Podfile against cocoapods {} block - Parse the Podfile and compare its pod entries with the pods declared in the Gradle cocoapods {} block. Record any dependencies that exist in the Podfile but are not listed in cocoapods {}. These Podfile-only dependencies still linked into the app via CocoaPods and must be migrated to swiftPMDependencies — dropping them silently causes obscure linkage errors at runtime.
  3. Check Xcode build phases - Open the .xcodeproj's project.pbxproj and search for the Gradle build phase script. Check if embedAndSignAppleFrameworkForXcode is present but commented out (prefixed with #). If commented out, it must be uncommented during Phase 5 — the integrateEmbedAndSign task may or may not handle this automatically.
  4. Check for existing Crashlytics dSYM upload script - If using FirebaseCrashlytics, search project.pbxproj for a dSYM upload shell script phase. Record its current path (CocoaPods-era scripts reference ${PODS_ROOT}/FirebaseCrashlytics/upload-symbols). This must be updated to the SPM path in Phase 5.
  5. Identify CocoaPods-related extras in build scripts - Search all build.gradle.kts files for CocoaPods workarounds beyond the standard cocoapods {} block (custom tasks hooking into podInstall, Pods.xcodeproj patching, podspec metadata, extraSpecAttributes, noPodspec(), etc.). See [cocoapods-extras-patterns.md](references/cocoapods-extras-patterns.md) for the full pattern list. Record all findings — these will be handled in Phase 6.

Phase 2: Gradle Configuration

Important scope note: Do NOT upgrade the Gradle wrapper version, update KSP, or update any other dependencies during this migration. Those are separate concerns and out of scope. Only change what is listed below.

2.1 Update Kotlin version

Skip this step if the project already uses Kotlin 2.4.0-Beta2 or later (recorded in Phase 1.0a).

Update to 2.4.0-Beta2 (or the latest available release with Swift Import support) in gradle/libs.versions.toml:

[versions]
kotlin = "2.4.0-Beta2"

2.4.0-Beta2 is available on Maven Central — no custom repository is needed.


Phase 3: Add swiftPMDependencies (Keep CocoaPods)

Do NOT remove the cocoapods {} block or kotlin("native.cocoapods") plugin yet. Add swiftPMDependencies {} alongside the existing CocoaPods configuration.

3.1 Add group property

group = "org.example.myproject"  // Required for import namespace

Compose Resources warning: If the project uses Compose Multiplatform resources (org.jetbrains.compose plugin or compose.resources), the group property is also used as the namespace for generated resource accessors (e.g., Res.string.*, Res.drawable.*). If group already exists in build.gradle.kts, do not change it. If you are adding group for the first time, warn the user that existing Compose resource accessor call sites throughout the project will change namespace and may need updating.

3.2 Add swiftPMDependencies block alongside cocoapods

For each pod dependency, add the equivalent SwiftPM package declaration. Use [common-pods-mapping.md](references/common-pods-mapping.md) to map each pod to its SPM package URL, product name, and importedClangModules.

Version preservation: Do NOT bump dependency versions during migration. Use the exact same version that was specified in the cocoapods {} block. Changing versions can resolve to different library builds that break cinterop APIs (removed symbols, changed signatures) and introduce issues unrelated to the migration itself.

| CocoaPods version spec | SPM equivalent | Example | |------------------------|---------------|---------| | version = "1.2.3" (exact) | version = "1.2.3" (simple) or exact("1.2.3") (typed) | pod("GoogleMaps") { version = "10.3.0" }version = "10.3.0" | | version = "~> 1.2" (optimistic) | version = "1.2.0" (simple) or from("1.2.0") (typed) | pod("FirebaseAuth") { version = "~> 12.5" }version = "12.5.0" | | No version specified | Ask user which version to pin | Ask the user which version to use |

Two API forms: The DSL has a simple string API and a typed API. Use the simple string API for most packages:

swiftPackage(url = "https://github.com/owner/repo.git", version = "1.0.0", products = listOf("ProductName"))

The simple API auto-defaults importedClangModules to the products list. Use the typed API (with url(), exact(), product() wrappers) only when you need exact version pinning, platform constraints, or explicit Clang module control. See [dsl-reference.md](references/dsl-reference.md) for the typed API.

Key concepts: products = SPM product names (controls linking). importedClangModules = Clang module names for cinterop bindings (only when discoverClangModulesImplicitly = false). discoverClangModulesImplicitly defaults to true (bindings for all Clang modules); set false when transitive C/C++ modules fail cinterop (Firebase, gRPC), then list needed modules explicitly.

Important: SPM product names and Clang module names don't always match. Always consult [common-pods-mapping.md](references/common-pods-mapping.md) for correct values.

Podfile-only dependencies: If Phase 1 step 9 identified dependencies that exist in the Podfile but not in the Gradle cocoapods {} block, these must also be added to swiftPMDependencies as products entries. Even though the KMP module didn't declare them, they were linked into the app by CocoaPods and may be required for the app to build. Look up each Podfile-only pod's SPM package URL and add it as a swiftPackage() with at least its products. If any of these pods were used via cinterop (check for import cocoapods.* statements referencing them), also add importedClangModules.

Do not mix the same library suite across CocoaPods and SPM. Libraries that share a common repository (e.g., all Firebase products) share transitive dependencies. Having some products linked via CocoaPods and others via SPM causes duplicate/conflicting symbols and dyld crashes at runtime. When migrating such a suite, move all pods from that suite to SPM at once — including Swift-only pods that Kotlin doesn't use directly. Add Swift-only pods as products entries (no importedClangModules needed). After adding new products, re-run integrateLinkagePackage to regenerate the linkage Swift package.

kotlin {
    // Keep existing targets
    iosArm64()
    iosSimulatorArm64()
    iosX64()

    swiftPMDependencies {
        iosMinimumDeploymentTarget = "16.0"

        swiftPackage(
            url = "https://github.com/owner/repo.git",
            version = "1.0.0",
            products = listOf("ProductName"),
        )
    }

    cocoapods {
        // ... keep existing cocoapods block for now
    }
}

3.3 Move framework configuration out of cocoapods block

If the cocoapods block contains a framework {} configuration, move it to the binaries API on each target. isStatic = true is recommended — dynamic frameworks have known edge cases with SwiftPM import that can cause linker errors, dyld crashes, or duplicate class warnings:

listOf(iosArm64(), iosSimulatorArm64(), iosX64()).forEach { iosTarget ->
    iosTarget.binaries.framework { baseName = "Shared"; isStatic = true }
}

If the cocoapods.framework {} block contain

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.