--- name: kotlin-tooling-cocoapods-spm-migration description: 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. license: Apache-2.0 metadata: short-description: "Migrate KMP from CocoaPods to SPM" author: JetBrains version: "1.0.0" source: https://github.com/Kotlin/kotlin-agent-skills/tree/main/skills/kotlin-tooling-cocoapods-spm-migration --- # CocoaPods to SwiftPM Migration for KMP Migrate Kotlin Multiplatform projects from `kotlin("native.cocoapods")` to `swiftPMDependencies {}` DSL. ## Requirements - **Kotlin**: Version with Swift Import support (e.g., 2.4.0-Beta1 or later) - **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`). 2. **Build only that module** (avoids building the entire multi-module project): ```bash ./gradlew :moduleName:build ``` Replace `moduleName` with the directory name of the module (e.g., `:shared:build`). 3. **If the targeted build 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 Ask the user: > Does your project already use a Kotlin version with Swift Import support (swiftPMDependencies DSL)? **If yes** → read their current Kotlin version from `gradle/libs.versions.toml` (or `build.gradle.kts`), record it, and skip Phase 2.2 (no version change needed). **If no** → ask: > Please provide the Kotlin version to use (e.g., "2.4.0", "2.4.0-Beta1", "2.4.0-dev-123"). Record the user-provided version. Then ask: > Does this Kotlin version require a custom Maven repository (e.g., JetBrains dev repo)? - **Yes** → ask for the repo URL (suggest `https://packages.jetbrains.team/maven/p/kt/dev` as default). Phase 2.1 will add it. - **No** → Phase 2.1 is skipped (no custom repo needed). Finally, check the project's current Kotlin version. Compare **major.minor** against the target. If it differs significantly (e.g., `2.1.0` → `2.4.0`), warn: "⚠️ Kotlin version jump — upgrading across minor versions can introduce breaking changes unrelated to this migration. Recommended: update first, verify it builds, then re-run." If the user confirms despite the mismatch, 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 pods provide native linking only — cinterop bindings come from a KMP wrapper library (e.g., `dev.gitlive:firebase-*`). See [common-pods-mapping.md](references/common-pods-mapping.md) for implications. 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 direct pod cinterop (and must be transformed). 6. **Map pods to SPM** - See [common-pods-mapping.md](references/common-pods-mapping.md) 7. **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 8. **Check for non-KMP CocoaPods** - Determine if the project uses CocoaPods for dependencies other than KMP. This affects cleanup strategy in Phase 5. 9. **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. 10. **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. 11. **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 Add custom Maven repository (if needed) **Skip this step** if the user indicated in Phase 1.0a that their Kotlin version does not require a custom Maven repository (i.e., it is an official release, Beta, or RC available from Maven Central). For dev/custom builds, add the custom Maven repository (URL from Phase 1.0a) to `settings.gradle.kts`: ```kotlin pluginManagement { repositories { gradlePluginPortal() mavenCentral() maven("") // ADD } } dependencyResolutionManagement { repositories { mavenCentral() maven("") // ADD } } ``` ### 2.2 Update Kotlin version **Skip this step** if the user's project already uses a Kotlin version with Swift Import support (recorded in Phase 1.0a). Update to the version recorded in Phase 1.0a: ```toml # gradle/libs.versions.toml [versions] kotlin = "" ``` ### 2.3 Add buildscript constraint (if swiftPMDependencies not recognized) ```kotlin // root build.gradle.kts buildscript { dependencies.constraints { "classpath"("org.jetbrains.kotlin:kotlin-gradle-plugin:!!") } } ``` Replace `` with the version recorded in Phase 1.0a. The `!!` suffix forces strict version resolution, ensuring no other dependency pulls in a different Kotlin Gradle plugin version. --- ## 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 ```kotlin group = "org.example.myproject" // Required for import namespace ``` ### 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 `importedModules`. **Key concepts:** `products` = SPM product names (controls linking). `importedModules` = Clang module names for cinterop bindings (only when `discoverModulesImplicitly = false`). `discoverModulesImplicitly` 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. **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 `importedModules` needed). After adding new products, re-run `integrateLinkagePackage` to regenerate the linkage Swift package. ```kotlin kotlin { // Keep existing targets iosArm64() iosSimulatorArm64() iosX64() swiftPMDependencies { iosDeploymentVersion.set("16.0") // If using KMP IntelliJ plugin, specify the .xcodeproj path: // xcodeProjectPathForKmpIJPlugin.set( // layout.projectDirectory.file("../iosApp/iosApp.xcodeproj") // ) swiftPackage( url = url("https://github.com/owner/repo.git"), version = from("1.0.0"), products = listOf(product("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: ```kotlin listOf(iosArm64(), iosSimulatorArm64(), iosX64()).forEach { iosTarget -> iosTarget.binaries.framework { baseName = "Shared"; isStatic = true } } ``` ### 3.4 Handle dev.gitlive/firebase-kotlin-sdk and similar CocoaPods-era KMP wrappers If the project uses `dev.gitlive:firebase-*` or similar KMP wrapper libraries, two additional steps are required: **A. Switch to `isStatic = true`** — dynamic frameworks + Firebase SPM = runtime `dyld` crash. After switching: re-run `integrateLinkagePackage`, remove any "Embed Frameworks" copy phase, move linker flags to `OTHER_LDFLAGS`. **B. Add framework search paths** — add conditional `-F` linkerOpts in `build.gradle.kts` and matching `FRAMEWORK_SEARCH_PATHS` in the Xcode project. See [common-pods-mapping.md](references/common-pods-mapping.md) § dev.gitlive and [troubleshooting.md](references/troubleshooting.md) for code snippets and the full product list. ### 3.5 Add opt-in for cinterop API ```kotlin kotlin.compilerOptions { optIn.add("kotlinx.cinterop.ExperimentalForeignApi") } ``` For full DSL reference, see [dsl-reference.md](references/dsl-reference.md). --- ## Phase 4: Kotlin Source Updates ### Import Namespace Formula ``` swiftPMImport... Where: - group: build.gradle.kts `group` property, dashes (-) → dots (.) - module: Gradle module name, dashes (-) → dots (.) - ClassName: Objective-C class name (FIR* for Firebase, GMS* for Google Maps) ``` ### Example Transformation ```kotlin // group = "org.jetbrains.kotlin.firebase.sample", module = "kotlin-library" // BEFORE: import cocoapods.FirebaseAnalytics.FIRAnalytics // AFTER: import swiftPMImport.org.jetbrains.kotlin.firebase.sample.kotlin.library.FIRAnalytics ``` **Import flattening:** The Clang module name (e.g., `FirebaseFirestoreInternal`, `FirebaseAuth`) disappears from the import path — all classes are flattened under the same `swiftPMImport..` prefix regardless of which library they come from. For example, both `cocoapods.FirebaseAuth.FIRAuth` and `cocoapods.FirebaseFirestoreInternal.FIRFirestore` become `swiftPMImport...FIRAuth` and `swiftPMImport...FIRFirestore`. ### Preserving Bundled Klib Imports > **CRITICAL:** Do NOT replace `cocoapods.*` imports that resolve to third-party KMP libraries' bundled cinterop klibs (identified in Phase 1 step 1.3). These imports must remain as-is — the `cocoapods` prefix is the package namespace in the library's published klib, not an actual CocoaPods dependency. The swiftPMDependencies cinterop generator skips modules already provided by a dependency's klib, so `swiftPMImport.*` for those classes will fail with "Unresolved reference". **Example** (project using [KMPNotifier](https://github.com/mirzemehdi/KMPNotifier)): ```kotlin // KEEP — resolves to kmpnotifier's bundled cinterop klib import cocoapods.FirebaseMessaging.FIRMessaging ``` ### Bulk Replacement Use a regex find-and-replace across all Kotlin source files, **excluding imports identified in Phase 1 step 1.3**: ``` Find: cocoapods\.\w+\. Replace: swiftPMImport... ``` After bulk replacement, **manually restore** any `cocoapods.*` imports that should be preserved (from bundled klibs). **Finding correct import path:** Run `./gradlew :moduleName:build` - errors show available classes. --- ## Phase 5: iOS Project Reconfiguration ### 5.1 Get migration command Build the CocoaPods workspace to obtain the migration command: ```bash cd /path/to/iosApp xcodebuild -scheme "$(echo -n *.xcworkspace | python3 -c 'import sys, json; from subprocess import check_output; print(list(set(json.loads(check_output(["xcodebuild", "-workspace", sys.stdin.readline(), "-list", "-json"]))["workspace"]["schemes"]) - set(json.loads(check_output(["xcodebuild", "-project", "Pods/Pods.xcodeproj", "-list", "-json"]))["project"]["schemes"]))[0])')" -workspace *.xcworkspace -destination 'generic/platform=iOS Simulator' ARCHS=arm64 | grep -A5 'What went wrong' ``` The build output will contain a command like: ```bash XCODEPROJ_PATH='/path/to/project/iosApp.xcodeproj' GRADLE_PROJECT_PATH=':shared' '/path/to/project/gradlew' -p '/path/to/project' ':shared:integrateEmbedAndSign' ':shared:integrateLinkagePackage' ``` Run this command. It modifies the `.xcodeproj` to trigger `embedAndSignAppleFrameworkForXcode` during the build. `integrateLinkagePackage` is a one-time setup — it does not need to be added as a build phase. If `integrateEmbedAndSign` is skipped, check for EmbedAndSign disablers (Phase 1 step 1.2) — remove them first, then re-run. **Verify `embedAndSignAppleFrameworkForXcode` is active:** After running integration, check the build phase script in `project.pbxproj`. If `embedAndSignAppleFrameworkForXcode` is commented out (prefixed with `#`), uncomment it. The `integrateLinkagePackage` task generates `_internal_linkage_SwiftPMImport/` at `/` — a local Swift package that mirrors your `products` list and ensures SPM libraries are linked into the final binary. After running the integration tasks, **disable User Script Sandboxing** (`ENABLE_USER_SCRIPT_SANDBOXING = NO`) in the `.xcodeproj`. Xcode 16+ enables it by default, which prevents the Gradle build phase from writing to the project directory: ```bash sed -i '' 's/ENABLE_USER_SCRIPT_SANDBOXING = YES/ENABLE_USER_SCRIPT_SANDBOXING = NO/g' "$XCODEPROJ_PATH/project.pbxproj" ``` If the setting is absent (Xcode defaults to YES), add `ENABLE_USER_SCRIPT_SANDBOXING = NO;` to the app target's `buildSettings` sections. Then restart the Gradle daemon: `./gradlew --stop` **Alternative (if xcodebuild approach fails):** See [troubleshooting.md](references/troubleshooting.md) § "Manual Integration Command Discovery" for a fallback script to discover paths and run integration tasks directly. ### 5.2 Update Crashlytics dSYM upload script (if applicable) If the project uses FirebaseCrashlytics and has a dSYM upload run script phase (identified in Phase 1 step 10), update the script path from `${PODS_ROOT}/FirebaseCrashlytics/upload-symbols` to `"${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run"`. See [troubleshooting.md](references/troubleshooting.md) § "Firebase Crashlytics: dSYM Upload Script" and [common-pods-mapping.md](references/common-pods-mapping.md) for the full script and input files list. ### 5.3 Deintegrate CocoaPods **Option A: Full deintegration** (if CocoaPods was used ONLY for KMP dependencies): Before deleting files, run `git status --short` and verify the paths. If unsure, move files to a backup location instead of deleting immediately. ```bash cd /path/to/iosApp pod deintegrate rm -rf Podfile Podfile.lock Pods/ # Remove the workspace that matches your app xcodeproj name XCODEPROJ_NAME=$(basename "$(find . -maxdepth 1 -name "*.xcodeproj" -type d | grep -v Pods | head -1)" .xcodeproj) rm -rf "${XCODEPROJ_NAME}.xcworkspace" # Return to project root cd .. # Remove the migrated module podspec only (for example, shared.podspec) # If unknown, list candidates and remove the matching one explicitly: ls -1 *.podspec # rm -f shared.podspec ``` This cleanup snippet is self-contained and does not assume `XCODEPROJ_PATH` or `GRADLE_PROJECT_PATH` from the earlier one-off migration command are still available in your shell. If `pod deintegrate` is not available, see [troubleshooting.md](references/troubleshooting.md) § "Manual CocoaPods Deintegration from pbxproj" for the full list of references to remove. Also remove `Pods/` from `.gitignore` and delete the `.xcworkspace` directory. **Option B: Partial removal** (if other non-KMP CocoaPods dependencies remain): Remove only the KMP pod line from the `Podfile` and re-run pod install: ```ruby target 'iosApp' do # Remove this line: pod 'shared', :path => '../shared' # Keep other non-KMP pods end ``` ```bash cd /path/to/iosApp && pod install ``` > **Tip:** Consider migrating remaining pods to SPM too — most popular iOS libraries support it natively. Add them in Xcode via File → Add Package Dependencies, then fully deintegrate CocoaPods once all pods are replaced. ### 5.4 Manual integration (if automatic fails) See [troubleshooting.md](references/troubleshooting.md) § "Manual Xcode Integration Steps" for the 5-step manual setup (build phase, sandboxing, linkage package). --- ## Phase 6: Remove CocoaPods from Gradle Now that the iOS project is reconfigured, remove the CocoaPods plugin and block: ### 6.1 Remove CocoaPods plugin ```kotlin plugins { // REMOVE: kotlin("native.cocoapods") alias(libs.plugins.kotlinMultiplatform) // Keep } ``` ### 6.2 Remove cocoapods block Delete the entire `cocoapods { ... }` block from `build.gradle.kts`. The `swiftPMDependencies {}` block and `binaries.framework {}` configuration added in Phase 3 replace it. ### 6.3 Remove deprecated gradle.properties entries If found in Phase 1.1, remove from `gradle.properties`: ```properties # REMOVE — no longer needed after migrating away from CocoaPods (KT-64096) kotlin.apple.deprecated.allowUsingEmbedAndSignWithCocoaPodsDependencies=true ``` ### 6.4 Clean up CocoaPods-related extras Review the extras identified in Phase 1 step 11. Podspec metadata, `noPodspec()`, CocoaPods task hooks, and `Pods.xcodeproj` patching code are **safe to remove** without user consultation. Non-standard pod configurations (`extraOpts`, `moduleName`), custom cinterop `defFile` setups, and CocoaPods-specific compiler/linker flags **require analysis** — consult the user if unsure whether SPM handles them automatically. See [cocoapods-extras-patterns.md](references/cocoapods-extras-patterns.md) for the full categorized list with examples. --- ## Phase 7: Verification ### 7.1 Build Gradle project Build the migrated module to verify the migration succeeded: ```bash ./gradlew :moduleName:build ``` ### 7.2 Link framework ```bash ./gradlew :moduleName:linkDebugFrameworkIosSimulatorArm64 ``` ### 7.3 Build iOS/macOS Xcode project After the Gradle build succeeds, build the Xcode project. Use `-project *.xcodeproj` if all CocoaPods were removed (Option A), or `-workspace *.xcworkspace` if non-KMP CocoaPods remain (Option B): ```bash cd /path/to/iosApp # Discover schemes and build (replace -project/-workspace as needed; for macOS use -destination 'platform=macOS'): xcodebuild -project *.xcodeproj -list -json 2>/dev/null | python3 -c "import sys,json; schemes=json.load(sys.stdin)['project']['schemes']; [print(s) for s in schemes]" xcodebuild -project *.xcodeproj -scheme "" -destination 'generic/platform=iOS Simulator' ARCHS=arm64 build ``` **If `checkSandboxAndWriteProtection` fails** — sandboxing was not disabled in Phase 5.1. Go back and apply the sandboxing fix from Phase 5.1, then retry. **If the pre-migration build was not verified** (Phase 1.0 fallback was used), warn the user: > Note: The pre-migration build could not be fully verified. If build errors appear now, some may be pre-existing issues unrelated to the migration. Compare errors against the pre-migration build output to distinguish migration issues from prior problems. ### If the build fails **Do NOT revert the migration.** Read the error log, re-check Phases 2-6, and consult [troubleshooting.md](references/troubleshooting.md). If unsure, present options to the user — do not silently undo migration work. --- ## Phase 8: Migration Report After migration (whether successful or not), write a comprehensive `MIGRATION_REPORT.md` in the project root. Use the template in [migration-report-template.md](references/migration-report-template.md). The report must include: 1. **Pre-Migration State** — CocoaPods dependencies (name, version, `linkOnly`), framework config, `cocoapods.*` imports, non-KMP pods, atypical configuration 2. **Migration Steps** — exact changes per phase with before/after snippets for non-trivial changes 3. **Import Transformations** — table of every import change, clearly marking preserved `cocoapods.*` imports and which bundled klib provides them 4. **Errors Encountered** — structured `Error #N` entries: phase, exact symptom, root cause, fix, generalizable flag 5. **Non-Trivial Decisions** — `isStatic` changes, preserved imports, framework search paths, trade-offs 6. **Files Changed** — complete list grouped by type (Gradle, Kotlin, Xcode, created, deleted) --- ## Additional Resources - [DSL Reference](references/dsl-reference.md) - Full swiftPMDependencies syntax - [Common Pods Mapping](references/common-pods-mapping.md) - Pod to SPM mapping table - [CocoaPods Extras Patterns](references/cocoapods-extras-patterns.md) - Detection and cleanup patterns for CocoaPods workarounds - [Troubleshooting](references/troubleshooting.md) - Issues, solutions, rollback - [Migration Report Template](references/migration-report-template.md) - Post-migration report template