Native Testing Strategy
bladeofgod/flutter-ai-harness
适用:设计或审查 Kotlin/Swift 原生模块、Bridge Adapter、Host 编译、模拟器/设备和系统能力验证。不适用:纯 Dart/Flutter 测试或用 Fake 代替相机和权限真机验证。触发词:JUnit、XCTest、Robolectric、instrumented test、Swift Testing、Framework Fake、Gradle…
Migrate KMP projects from CocoaPods (kotlin("native.cocoapods")) to Swift Package Manager (swiftPMDependencies DSL) — replaces pod() with swiftPackage(), transforms cocoapods.
$ npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install JetBrains/skills kotlin-tooling-cocoapods-spm-migration --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/JetBrains/skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/kotlin-tooling-cocoapods-spm-migration .claude/skills/kotlin-tooling-cocoapods-spm-migration && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "kotlin-tooling-cocoapods-spm-migration" agent skill from https://github.com/JetBrains/skills/tree/main/kotlin-tooling-cocoapods-spm-migration into .claude/skills/kotlin-tooling-cocoapods-spm-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kotlin-tooling-cocoapods-spm-migration", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/JetBrains/skills/tree/main/kotlin-tooling-cocoapods-spm-migrationType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install JetBrains/skills kotlin-tooling-cocoapods-spm-migration --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/JetBrains/skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/kotlin-tooling-cocoapods-spm-migration .agents/skills/kotlin-tooling-cocoapods-spm-migration && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "kotlin-tooling-cocoapods-spm-migration" agent skill from https://github.com/JetBrains/skills/tree/main/kotlin-tooling-cocoapods-spm-migration into .agents/skills/kotlin-tooling-cocoapods-spm-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kotlin-tooling-cocoapods-spm-migration", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install JetBrains/skills kotlin-tooling-cocoapods-spm-migration --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/JetBrains/skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/kotlin-tooling-cocoapods-spm-migration .cursor/skills/kotlin-tooling-cocoapods-spm-migration && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "kotlin-tooling-cocoapods-spm-migration" agent skill from https://github.com/JetBrains/skills/tree/main/kotlin-tooling-cocoapods-spm-migration into .cursor/skills/kotlin-tooling-cocoapods-spm-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kotlin-tooling-cocoapods-spm-migration", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/JetBrains/skills.git --path kotlin-tooling-cocoapods-spm-migration--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install JetBrains/skills kotlin-tooling-cocoapods-spm-migration --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/JetBrains/skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/kotlin-tooling-cocoapods-spm-migration .gemini/skills/kotlin-tooling-cocoapods-spm-migration && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "kotlin-tooling-cocoapods-spm-migration" agent skill from https://github.com/JetBrains/skills/tree/main/kotlin-tooling-cocoapods-spm-migration into .gemini/skills/kotlin-tooling-cocoapods-spm-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kotlin-tooling-cocoapods-spm-migration", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install JetBrains/skills kotlin-tooling-cocoapods-spm-migrationInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/JetBrains/skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/kotlin-tooling-cocoapods-spm-migration .github/skills/kotlin-tooling-cocoapods-spm-migration && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "kotlin-tooling-cocoapods-spm-migration" agent skill from https://github.com/JetBrains/skills/tree/main/kotlin-tooling-cocoapods-spm-migration into .github/skills/kotlin-tooling-cocoapods-spm-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kotlin-tooling-cocoapods-spm-migration", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install JetBrains/skills kotlin-tooling-cocoapods-spm-migration --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/JetBrains/skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/kotlin-tooling-cocoapods-spm-migration .opencode/skills/kotlin-tooling-cocoapods-spm-migration && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "kotlin-tooling-cocoapods-spm-migration" agent skill from https://github.com/JetBrains/skills/tree/main/kotlin-tooling-cocoapods-spm-migration into .opencode/skills/kotlin-tooling-cocoapods-spm-migration/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kotlin-tooling-cocoapods-spm-migration", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
kotlin-tooling-cocoapods-spm-migrationMigrate KMP projects from CocoaPods (kotlin("native.cocoapods")) to Swift Package Manager (swiftPMDependencies DSL) — replaces pod() with swiftPackage(), transforms cocoapods.
Kotlin Tooling Cocoapods Spm Migration is an agent skill from JetBrains/skills, published by the product's own GitHub organization. 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.
Its SKILL.md is about 6.5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 6 other files, including reference files (for example `references/cocoapods-extras-patterns.md`, `references/common-pods-mapping.md` and `references/dsl-reference.md`).
It sits in Mobile, covering Android development and iOS development. It works with Kotlin, Swift, Xcode and Gradle. The repository describes itself as: Curated agent skills collection verified by JetBrains. The licence is Apache-2.0.
8 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit e0f258b. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
xcodebuildpython3gitFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
github.compackages.jetbrains.teamAlso links to:
youtrack.jetbrains.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Kotlin Tooling Cocoapods Spm Migration loads about 6.5k tokens when it runs, and up to ~22k if it reads all its reference files. Until then it costs about 70 tokens; SKILL.md has 2,517 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from JetBrains/skills at commit e0f258b, republished under its Apache-2.0 licence (© JetBrains). 2,517 words, ~6,532 tokens.
.claude/skills/kotlin-tooling-cocoapods-spm-migration/SKILL.md (or your agent's skills folder). This skill also uses 5 other files; get the full folder from GitHub.Migrate Kotlin Multiplatform projects from kotlin("native.cocoapods") to swiftPMDependencies {} DSL.
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 |
Before starting migration, identify the module to migrate and confirm it compiles successfully.
Find the module that uses CocoaPods — look for build.gradle.kts files containing cocoapods:
grep -rl "cocoapods" --include="build.gradle.kts" .Extract the module name from the path (e.g., ./shared/build.gradle.kts → module name is shared).
Build only that module (avoids building the entire multi-module project):
./gradlew :moduleName:buildReplace moduleName with the directory name of the module (e.g., :shared:build).
If the targeted build fails, ask the user to either:
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).
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)?
https://packages.jetbrains.team/maven/p/kt/dev as default). Phase 2.1 will add it.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.
Search gradle.properties for the deprecated property:
kotlin.apple.deprecated.allowUsingEmbedAndSignWithCocoaPodsDependencies=trueThis 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.
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 § "integrateEmbedAndSign Skipped" for patterns.
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 § "Third-Party KMP Libraries with Bundled Klibs".
Find and record:
cocoapods in build.gradle.kts filescocoapods {} blocksbaseName, isStatic, deployment target from cocoapods.framework {}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 for implications.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).Podfile and .xcworkspace:find . -name "Podfile" -type fiosApp/, ios/, or project root) - needed for Phase 5.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.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.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 for the full pattern list. Record all findings — these will be handled in Phase 6.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.
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:
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
maven("<custom-repo-url>") // ADD
}
}
dependencyResolutionManagement {
repositories {
mavenCentral()
maven("<custom-repo-url>") // ADD
}
}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:
# gradle/libs.versions.toml
[versions]
kotlin = "<kotlin-version>"// root build.gradle.kts
buildscript {
dependencies.constraints {
"classpath"("org.jetbrains.kotlin:kotlin-gradle-plugin:<kotlin-version>!!")
}
}Replace <kotlin-version> 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.
Do NOT remove the cocoapods {} block or kotlin("native.cocoapods") plugin yet. Add swiftPMDependencies {} alongside the existing CocoaPods configuration.
group = "org.example.myproject" // Required for import namespaceFor each pod dependency, add the equivalent SwiftPM package declaration. Use 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 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 {
// 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
}
}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 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 § dev.gitlive and troubleshooting.md for code snippets and the full product list.
kotlin.compilerOptions {
optIn.add("kotlinx.cinterop.ExperimentalForeignApi")
}For full DSL reference, see dsl-reference.md.
swiftPMImport.<group>.<module>.<ClassName>
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)// group = "org.jetbrains.kotlin.firebase.sample", module = "kotlin-library"
// BEFORE:
import cocoapods.FirebaseAnalytics.FIRAnalytics
// AFTER:
import swiftPMImport.org.jetbrains.kotlin.firebase.sample.kotlin.library.FIRAnalyticsImport flattening: The Clang module name (e.g., FirebaseFirestoreInternal, FirebaseAuth) disappears from the import path — all classes are flattened under the same swiftPMImport.<group>.<module> prefix regardless of which library they come from. For example, both cocoapods.FirebaseAuth.FIRAuth and cocoapods.FirebaseFirestoreInternal.FIRFirestore become swiftPMImport.<group>.<module>.FIRAuth and swiftPMImport.<group>.<module>.FIRFirestore.
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 — thecocoapodsprefix 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, soswiftPMImport.*for those classes will fail with "Unresolved reference".
Example (project using KMPNotifier):
// KEEP — resolves to kmpnotifier's bundled cinterop klib
import cocoapods.FirebaseMessaging.FIRMessagingUse a regex find-and-replace across all Kotlin source files, excluding imports identified in Phase 1 step 1.3:
Find: cocoapods\.\w+\.
Replace: swiftPMImport.<your.group>.<your.module>.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.
Build the CocoaPods workspace to obtain the migration command:
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:
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 <iosDir>/ — 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:
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 § "Manual Integration Command Discovery" for a fallback script to discover paths and run integration tasks directly.
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 § "Firebase Crashlytics: dSYM Upload Script" and common-pods-mapping.md for the full script and input files list.
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.
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.podspecThis 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 § "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:
target 'iosApp' do
# Remove this line:
pod 'shared', :path => '../shared'
# Keep other non-KMP pods
endcd /path/to/iosApp && pod installTip: 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.
See troubleshooting.md § "Manual Xcode Integration Steps" for the 5-step manual setup (build phase, sandboxing, linkage package).
Now that the iOS project is reconfigured, remove the CocoaPods plugin and block:
plugins {
// REMOVE: kotlin("native.cocoapods")
alias(libs.plugins.kotlinMultiplatform) // Keep
}Delete the entire cocoapods { ... } block from build.gradle.kts. The swiftPMDependencies {} block and binaries.framework {} configuration added in Phase 3 replace it.
If found in Phase 1.1, remove from gradle.properties:
# REMOVE — no longer needed after migrating away from CocoaPods (KT-64096)
kotlin.apple.deprecated.allowUsingEmbedAndSignWithCocoaPodsDependencies=trueReview 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 for the full categorized list with examples.
Build the migrated module to verify the migration succeeded:
./gradlew :moduleName:build./gradlew :moduleName:linkDebugFrameworkIosSimulatorArm64After 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):
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 "<AppScheme>" -destination 'generic/platform=iOS Simulator' ARCHS=arm64 buildIf 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.
Do NOT revert the migration. Read the error log, re-check Phases 2-6, and consult troubleshooting.md. If unsure, present options to the user — do not silently undo migration work.
After migration (whether successful or not), write a comprehensive MIGRATION_REPORT.md in the project root. Use the template in migration-report-template.md.
The report must include:
linkOnly), framework config, cocoapods.* imports, non-KMP pods, atypical configurationcocoapods.* imports and which bundled klib provides themError #N entries: phase, exact symptom, root cause, fix, generalizable flagisStatic changes, preserved imports, framework search paths, trade-offs© JetBrains, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 5 other files (references) in kotlin-tooling-cocoapods-spm-migration of JetBrains/skills.
Open the folder on GitHubat commit e0f258b
We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in JetBrains/skills, which our catalogue first saw on October 7, 2026.
Kotlin Tooling Cocoapods Spm Migration next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Kotlin Tooling Cocoapods Spm Migration this skillJetBrains/skills | 366 | 1 repos | ~6.5k | Automated safety check: Pass | Apache-2.0 | |
| Native Testing Strategybladeofgod/flutter-ai-harness | 116 | — | ~345 | Automated safety check: Pass | MIT | |
| Expo Brownfield Integrationmweinbach/agent-coworker | 156 | 2 repos | ~900 | Automated safety check: Notes | Custom licence | |
| Swift iOS Standardsbladeofgod/flutter-ai-harness | 116 | — | ~375 | Automated safety check: Pass | MIT | |
| macOS Spm App PackagingDimillian/Skills | 4k | 5 repos | ~1.2k | Automated safety check: Pass | MIT | |
| Build Teaql Appteaql/teaql-agent-kit | 2.8k | — | ~4.6k | Automated safety check: Pass | MIT |
bladeofgod/flutter-ai-harness
适用:设计或审查 Kotlin/Swift 原生模块、Bridge Adapter、Host 编译、模拟器/设备和系统能力验证。不适用:纯 Dart/Flutter 测试或用 Fake 代替相机和权限真机验证。触发词:JUnit、XCTest、Robolectric、instrumented test、Swift Testing、Framework Fake、Gradle…
mweinbach/agent-coworker
Helps add Expo and React Native to an existing native iOS or Android app, and choose between a prebuilt AAR or XCFramework and a fully integrated build.
bladeofgod/flutter-ai-harness
适用:编写或审查 iOS Swift Native Module、Bridge Adapter、Host 接线、Info.plist/Entitlements、SwiftPM 和原生 UI。不适用:Kotlin/Android、Dart Client 或 Wire Contract…
Dimillian/Skills
Scaffold, build, and package SwiftPM-based macOS apps without an Xcode project.
teaql/teaql-agent-kit
Build or change a TeaQL application in Java, Rust, Go, Swift, Python, C/.NET, or TypeScript, including Kotlin/JVM applications that consume Java-generated libraries.
microsoft/testfx
MANDATORY for static source-to-test pairing: find or list source files/modules without corresponding tests, or suggest test locations from repository structure.
JetBrains/skills
A skill your agent uses when the task requires automating a real browser from the terminal (navigation, form filling, snapshots, screenshots, data extraction, UI-flow debugging) via playwright-cli…
JetBrains/skills
Guide for creating effective skills. An agent skill from JetBrains/skills.
JetBrains/skills
Transcribe audio files to text with optional diarization and known-speaker hints.
JetBrains/skills
A skill your agent uses when the user asks to generate or edit images via the OpenAI Image API (for example: generate image, edit/inpaint/mask, background removal or replacement, transparent…
JetBrains/skills
A skill your agent uses when the user asks for text-to-speech narration or voiceover, accessibility reads, audio prompts, or batch speech generation via the OpenAI Audio API; run the bundled CLI…
JetBrains/skills
A skill your agent uses when the user explicitly asks for a desktop or system screenshot (full screen, specific app or window, or a pixel region), or when tool-specific capture capabilities are…
Categories
Migrate KMP projects from CocoaPods (kotlin("native.cocoapods")) to Swift Package Manager (swiftPMDependencies DSL) — replaces pod() with swiftPackage(), transforms cocoapods. Kotlin Tooling Cocoapods Spm Migration is an agent skill from JetBrains/skills, published by the product's own GitHub organization.cocoapods")) to Swift Package Manager (swiftPMDependencies DSL) — replaces pod() with swiftPackage(), transforms cocoapods.
Kotlin Tooling Cocoapods Spm Migration fits situations like: tasks that involve Android development; tasks that involve iOS development.
Run `npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a claude-code`. Or copy the skill folder (kotlin-tooling-cocoapods-spm-migration in JetBrains/skills) into .claude/skills/kotlin-tooling-cocoapods-spm-migration in your project. Claude Code loads it when a task matches its description.
Run `npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a codex`. Or copy the skill folder (kotlin-tooling-cocoapods-spm-migration in JetBrains/skills) into .agents/skills/kotlin-tooling-cocoapods-spm-migration in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add JetBrains/skills --skill kotlin-tooling-cocoapods-spm-migration -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/kotlin-tooling-cocoapods-spm-migration, .gemini/skills/kotlin-tooling-cocoapods-spm-migration, .github/skills/kotlin-tooling-cocoapods-spm-migration and .opencode/skills/kotlin-tooling-cocoapods-spm-migration in your project.
Going by SKILL.md and its folder, Kotlin Tooling Cocoapods Spm Migration needs the command-line tools its instructions call (xcodebuild, python3 and git).
SKILL.md names 3 domains. In commands or code: github.com and packages.jetbrains.team; the agent is likely to contact these when it follows the instructions. As links in the text: youtrack.jetbrains.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Kotlin Tooling Cocoapods Spm Migration is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.5k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 15k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Kotlin Tooling Cocoapods Spm Migration: Native Testing Strategy (bladeofgod/flutter-ai-harness, 116 stars), Expo Brownfield Integration (mweinbach/agent-coworker, 156 stars), Swift iOS Standards (bladeofgod/flutter-ai-harness, 116 stars) and macOS Spm App Packaging (Dimillian/Skills, 4k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
JetBrains (a GitHub organization, an official publisher) maintains it in JetBrains/skills, which has 366 GitHub stars. The repository holds 76 skills in this directory. The repository was last updated on June 29, 2026.
Source: JetBrains/skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.