mcpField
K
kotlin-tooling-cocoapods-spm-migration

kotlin-tooling-cocoapods-spm-migration

github.com/kotlin開発1.1k1.9k 回インストール7 週間前に更新
リポジトリ

kotlin-tooling-cocoapods-spm-migration について

Kotlin Multiplatform プロジェクトを CocoaPods Gradle プラグインから `swiftPMDependencies {}` DSL へ移行するスキルです。CocoaPods を残したまま SwiftPM を設定し、プロジェクトの再構成、整理、検証へ進みます。

主な機能

  1. 01CocoaPods とプロジェクト設定の分析
  2. 02CocoaPods と並行した SwiftPM 設定
  3. 03Kotlin インポートの変換
  4. 04iOS プロジェクトの再構成
  5. 05CocoaPods の削除とビルド検証

活用シーン

  1. 01KMP プロジェクトの CocoaPods 依存置換
  2. 02iOS フレームワーク依存の SwiftPM 移行
  3. 03インポートと Podfile の対応確認
  4. 04移行後の Gradle と Xcode ビルド検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/kotlin/kotlin-agent-skills --skill kotlin-tooling-cocoapods-spm-migration

インストール後、Claude Code で/kotlin-tooling-cocoapods-spm-migrationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
cocoapods-extras-patterns.md
common-pods-mapping.md
dsl-reference.md
migration-report-template.md
troubleshooting.md

詳細

KMP の CocoaPods から SwiftPM への移行

Kotlin Multiplatform プロジェクトを kotlin("native.cocoapods") から swiftPMDependencies {} DSL に移行します。

要件

  • Kotlin:2.4.0-Beta2 以降(swiftPMDependencies をサポートする最初の公開リリース。Maven Central で利用可能)
  • Xcode:16.4 または 26.0 以降
  • iOS デプロイメントターゲット:16.0 以降を推奨

移行の概要

重要:Phase 6 までは cocoapods {} ブロックとプラグインを有効なままにしてください。まず既存の CocoaPods 設定に swiftPMDependencies {} を追加し、Xcode を再設定してから、CocoaPods を削除します。

Phase操作
1既存の CocoaPods 設定を分析する
2Gradle の設定(リポジトリ、Kotlin バージョン)を更新する
3既存の cocoapods {} と並行して swiftPMDependencies {} を追加する
4Kotlin のインポートを変換する
5iOS プロジェクトを再設定し、CocoaPods を deintegrate する
6Gradle から CocoaPods プラグインを削除する
7Gradle ビルドと Xcode プロジェクトのビルドを確認する
8MIGRATION_REPORT.md を作成する

Phase 1:移行前の分析

1.0 プロジェクトがビルドできることを確認する

移行を開始する前に、移行対象のモジュールを特定し、正常にコンパイルできることを確認します。

  1. CocoaPods を使用しているモジュールを見つける — cocoapods を含む build.gradle.kts ファイルを探します:

    grep -rl "cocoapods" --include="build.gradle.kts" .
    

    パスからモジュール名を抽出します(例:./shared/build.gradle.kts → モジュール名は shared)。注:複数のモジュールで CocoaPods を使用している場合があります。該当するものをすべて記録してください。通常、iOS アプリにリンクされるフレームワークを生成するモジュールだけに swiftPMDependencies が必要で、その他のモジュールでは CocoaPods の削除のみが必要です(Phase 6)。

  2. Kotlin コードをコンパイルする — モジュールの Kotlin コンパイルタスクを実行し、Kotlin ソースがコンパイルできることを確認します:

    ./gradlew :moduleName:compileKotlinIosSimulatorArm64
    

    moduleName をモジュールのディレクトリ名に置き換えます(例::shared:compileKotlinIosSimulatorArm64)。リリースリンクも実行する完全な build より高速で、Kotlin コードの正しさを確認するには十分です。

  3. iOS アプリをビルドする(任意) — Xcode プロジェクトを探してビルドし、アプリ全体をコンパイルできることを確認します:

    # 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
    

    ユーザーが Xcode のビルドを省略したい場合、または Xcode プロジェクトが見つからない場合は、ビルドせずに続行してください。手順 2 の Kotlin コンパイルで続行するには十分です。

  4. Kotlin のコンパイルに失敗した場合は、次のいずれかをユーザーに依頼します:

    • モジュールのビルドを確認するための正しい Gradle コマンドを提示する
    • モジュールが正常に動作しており、安全に続行できることを確認する

    ユーザーがビルドコマンドを提示せずに確認した場合は、移行前のビルドを確認できなかったことを記録し、移行の最後に警告してください(Phase 7)。

1.0a Swift インポートをサポートする Kotlin バージョンを確認する

gradle/libs.versions.toml(または build.gradle.kts)から現在の Kotlin バージョンを読み取ります。

プロジェクトがすでに Kotlin 2.4.0-Beta2 以降を使用している場合 → バージョンを記録し、Phase 2.1 をスキップします(バージョン変更は不要です)。

プロジェクトがそれより古い Kotlin バージョンを使用している場合 → Phase 2.1 で 2.4.0-Beta2 にアップグレードします(swiftPMDependencies をサポートする最初の公開リリース。Maven Central で利用可能で、カスタムリポジトリは不要です)。ユーザーに次のように警告します:「⚠️ Kotlin のバージョン変更 — マイナーバージョンをまたいでアップグレードすると、この移行とは無関係な破壊的変更が発生する可能性があります。推奨手順:まず更新してビルドを確認し、その後この移行を再実行してください。」ユーザーが確認したら続行します。

1.1 非推奨の CocoaPods 回避策プロパティを確認する

gradle.properties で、次の非推奨プロパティを検索します:

kotlin.apple.deprecated.allowUsingEmbedAndSignWithCocoaPodsDependencies=true

このプロパティは、CocoaPods 依存関係と併せて embedAndSign を使用するプロジェクト向けの回避策でした(KT-64096 を参照)。サポートされていない設定に関するエラーを抑制します。このエラーは、実行時クラッシュやシンボルの重複を引き起こす可能性があります。SwiftPM インポートへの移行後、このプロパティは不要になるため、Phase 6 で削除する必要があります。見つかった場合は、その存在を記録してください。

1.2 EmbedAndSign を無効にするコードを確認する

すべての build.gradle.kts ファイルを検索し、EmbedAndSign タスクを無効にするコード(例:TaskGraph.whenReady フィルター、tasks.matching ブロック)を探してください。これは CocoaPods 時代の回避策であり、integrateEmbedAndSign(フェーズ 5 で必要)も無効になるため、移行を妨げます。該当するコードを記録してください。フェーズ 6 では必ず削除する必要があり、それより前に削除が必要になる場合もあります。パターンについては、troubleshooting.md §「integrateEmbedAndSign Skipped」を参照してください。

1.3 cinterop klib を同梱するサードパーティ製 KMP ライブラリを確認する

一部の KMP ライブラリは、cocoapods.* パッケージ名前空間を持つビルド済みの cinterop klib を同梱しています。移行後、swiftPMDependencies の cinterop ジェネレーターは重複を避けるため、既存のバインディングを検出した Clang モジュールについては新しいバインディングの生成をスキップします。そのため、該当するモジュールの cocoapods.* インポートはそのまま維持する必要があります。これらは実際の CocoaPods ではなく、サードパーティ製ライブラリに同梱された klib に解決されます。

cocoapods.* klib を同梱する既知のライブラリ:

ライブラリMaven アーティファクト同梱 klib の名前空間提供されるクラス
KMPNotifierio.github.mirzemehdi:kmpnotifiercocoapods.FirebaseMessagingFIRMessaging、FIRMessagingAPNSTokenType など

検出方法: Gradle の依存関係宣言で既知のライブラリを検索し、同梱されている名前空間をステップ 4 で見つかった import cocoapods.* 文と照合してください。一致するものがあれば記録してください。これらのインポートはフェーズ 4 では変換されません。

サードパーティ製 KMP ライブラリが cinterop klib を同梱しているかどうか不明な場合は、プロジェクトに linkOnly = true の pod 依存関係があるか確認してください。これは、そのライブラリが該当クラス用の独自 klib を提供している有力な手がかりです。

klib の内容を調べて同梱されたバインディングを確認する方法については、troubleshooting.md §「Third-Party KMP Libraries with Bundled Klibs」を参照してください。

見つけて記録する項目:

  1. CocoaPods の設定 - build.gradle.kts ファイル内で cocoapods を検索します
  2. Pod の依存関係 - cocoapods {} ブロックから Pod 名とバージョンを抽出します
  3. フレームワークの設定 - cocoapods.framework {} から baseName、isStatic、デプロイメントターゲットを記録します
  4. linkOnly Pod - linkOnly = true を指定して宣言されている Pod を記録します。よくあるパターンは次の 2 つです。
    • KMP ラッパーライブラリ(例:dev.gitlive:firebase-*):ラッパーが Kotlin API を提供し、Pod はリンクされるだけです。詳しくは、影響について common-pods-mapping.md を参照してください。
    • マルチモジュールプロジェクト:子モジュールがすでにその Pod の cinterop バインディングを提供しているため、利用側のモジュールが linkOnly = true を宣言します。SwiftPM では、swiftPackage() の宣言は Pod を直接使用する子モジュールにのみ記述してください。利用側のモジュールで同じパッケージを再宣言してはいけません。これには、それらのパッケージを含まない swiftPMDependencies {} ブロック(すべての Pod が linkOnly の場合は空のブロック)だけが必要です。インポート名前空間への影響: 利用側のモジュールが、子モジュールの swiftPMDependencies に由来する SPM クラスをインポートする場合、インポートパスの名前空間には、子モジュールのグループ名とモジュール名を使用します(Phase 4 の Import Namespace Formula を参照)。
  5. Kotlin のインポート - すべての import cocoapods.* 文を検索します。手順 1.3 と照合し、バンドル済みの klib に由来する(保持する必要がある)インポートと、Pod の直接的な cinterop に由来する(変換が必要な)インポートを特定します。
  6. Pod を SPM にマッピング - common-pods-mapping.md を参照してください
  7. iOS プロジェクトのディレクトリを特定 - Podfile と .xcworkspace が含まれるディレクトリを探します。
    find . -name "Podfile" -type f
    
    このパス(例:iosApp/、ios/、またはプロジェクトのルート)を記録します。Phase 5 で必要になります。
  8. KMP 以外の CocoaPods を確認 - プロジェクトが KMP 以外の依存関係にも CocoaPods を使用しているかを確認します。これは Phase 5 のクリーンアップ方法に影響します。
  9. Podfile と cocoapods {} ブロックを照合 - Podfile を解析し、そこにある Pod エントリと Gradle の cocoapods {} ブロックで宣言されている Pod を比較します。Podfile には存在するものの、cocoapods {} には記載されていない依存関係を記録します。Podfile のみに存在するこれらの依存関係も、CocoaPods 経由でアプリにリンクされているため、swiftPMDependencies に移行する必要があります。これらを黙って削除すると、実行時に原因の特定が難しいリンクエラーが発生します。
  10. Xcode のビルドフェーズを確認 - .xcodeproj の project.pbxproj を開き、Gradle のビルドフェーズスクリプトを検索します。embedAndSignAppleFrameworkForXcode が存在するものの、# が先頭に付いてコメントアウトされているかどうかを確認します。コメントアウトされている場合は、Phase 5 でコメントを解除する必要があります。integrateEmbedAndSign タスクがこれを自動的に処理するかどうかは、状況によって異なります。
  11. 既存の Crashlytics dSYM アップロードスクリプトを確認 - FirebaseCrashlytics を使用している場合、project.pbxproj で dSYM アップロード用のシェルスクリプトフェーズを検索します。現在のパスを記録します(CocoaPods 時代のスクリプトでは ${PODS_ROOT}/FirebaseCrashlytics/upload-symbols が参照されています)。Phase 5 で SPM のパスに更新する必要があります。
  12. ビルドスクリプト内の CocoaPods 関連の追加要素を特定 - すべての build.gradle.kts ファイルを検索し、標準の cocoapods {} ブロック以外の CocoaPods 回避策(podInstall にフックするカスタムタスク、Pods.xcodeproj のパッチ適用、podspec メタデータ、extraSpecAttributes、noPodspec() など)を確認します。パターンの全一覧は cocoapods-extras-patterns.md を参照してください。調査結果をすべて記録します。これらは Phase 6 で対応します。

Phase 2:Gradle の設定

重要な対象範囲に関する注意: この移行中に Gradle ラッパーのバージョンをアップグレードしたり、KSP やその他の依存関係を更新したりしてはいけません。これらは別の検討事項であり、今回の対象範囲外です。以下に記載した項目のみを変更してください。

2.1 Kotlin バージョンを更新する

プロジェクトで Kotlin 2.4.0-Beta2 以降をすでに使用している場合は、この手順をスキップしてください(Phase 1.0a で記録済み)。

gradle/libs.versions.toml で 2.4.0-Beta2(または Swift Import をサポートする最新のリリース)に更新します。

[versions]
kotlin = "2.4.0-Beta2"

2.4.0-Beta2 は Maven Central で利用できます。カスタムリポジトリは必要ありません。


Phase 3: swiftPMDependencies を追加する(CocoaPods は維持)

まだ cocoapods {} ブロックや kotlin("native.cocoapods") プラグインを削除しないでください。 既存の CocoaPods 設定に加えて swiftPMDependencies {} を追加します。

3.1 group プロパティを追加する
group = "org.example.myproject"  // Required for import namespace

Compose Resources に関する警告: プロジェクトで Compose Multiplatform リソース(org.jetbrains.compose プラグインまたは compose.resources)を使用している場合、group プロパティは生成されたリソースアクセサー(例:Res.string.*、Res.drawable.*)の名前空間としても使用されます。build.gradle.kts にすでに group がある場合は、変更しないでください。初めて group を追加する場合は、プロジェクト全体にある既存の Compose リソースアクセサーの呼び出し箇所で名前空間が変わり、更新が必要になる可能性があることをユーザーに警告してください。

3.2 cocoapods と並行して swiftPMDependencies ブロックを追加する

各 Pod 依存関係について、対応する SwiftPM パッケージ宣言を追加します。common-pods-mapping.md を参照し、各 Pod の SPM パッケージ URL、プロダクト名、importedClangModules を確認してください。

バージョンの維持: 移行中に依存関係のバージョンを上げないでください。cocoapods {} ブロックで指定されているものとまったく同じバージョンを使用してください。バージョンを変更すると、異なるライブラリビルドが解決され、cinterop API の破損(シンボルの削除、シグネチャの変更)が発生したり、移行自体とは無関係な問題が持ち込まれたりする可能性があります。

CocoaPods のバージョン指定SPM での指定例
version = "1.2.3"(完全一致)version = "1.2.3"(シンプル)または exact("1.2.3")(型指定)pod("GoogleMaps") { version = "10.3.0" } → version = "10.3.0"
version = "~> 1.2"(オプティミスティック)version = "1.2.0"(シンプル)または from("1.2.0")(型指定)pod("FirebaseAuth") { version = "~> 12.5" } → version = "12.5.0"
バージョン指定なし固定するバージョンをユーザーに確認する使用するバージョンをユーザーに確認する

2 種類の API: DSL にはシンプルな文字列 API と型指定 API があります。ほとんどのパッケージではシンプルな文字列 API を使用してください。

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

シンプルな API では、importedClangModules は products リストに自動的に設定されます。型指定 API(url()、exact()、product() のラッパーを使用)は、バージョンを完全一致で固定する必要がある場合、プラットフォームの制約がある場合、または Clang モジュールを明示的に制御する場合にのみ使用してください。型指定 API については dsl-reference.md を参照してください。

重要な概念: products = SPM のプロダクト名(リンクを制御します)。importedClangModules = cinterop バインディング用の Clang モジュール名(discoverClangModulesImplicitly = false の場合のみ)。discoverClangModulesImplicitly のデフォルトは true(すべての Clang モジュールのバインディングを生成)です。推移的な C/C++ モジュールが cinterop で失敗する場合(Firebase、gRPC)は false を設定し、必要なモジュールを明示的に列挙します。

重要: SPM のプロダクト名と Clang モジュール名は、常に一致するとは限りません。正しい値を確認するには、必ず common-pods-mapping.md を参照してください。

Podfile のみに記載されている依存関係: Phase 1 の手順 9 で、Podfile には存在するものの Gradle の cocoapods {} ブロックにはない依存関係が特定された場合は、それらも products エントリとして swiftPMDependencies に追加する必要があります。KMP モジュールで宣言されていなくても、CocoaPods によってアプリにリンクされており、アプリのビルドに必要な可能性があります。Podfile のみに記載されている各 Pod の SPM パッケージ URL を調べ、少なくとも products を含む swiftPackage() として追加してください。これらの Pod が cinterop 経由で使われていた場合(該当する Pod を参照する import cocoapods.* 文を確認してください)は、importedClangModules も追加してください。

同じライブラリスイートを CocoaPods と SPM に混在させないでください。 共通のリポジトリを共有するライブラリ(例:すべての Firebase プロダクト)は、推移的依存関係も共有します。一部のプロダクトを CocoaPods 経由で、その他を SPM 経由でリンクすると、シンボルの重複や競合が起き、実行時に dyld クラッシュが発生します。このようなスイートを移行するときは、Kotlin から直接使われない Swift 専用 Pod も含め、スイート内のすべての Pod を一度に SPM に移してください。Swift 専用 Pod は products エントリとして追加してください(importedClangModules は不要です)。新しいプロダクトの追加後、integrateLinkagePackage を再実行して、リンケージ Swift パッケージを再生成してください。

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 cocoapods ブロックからフレームワーク設定を移動する

cocoapods ブロックに framework {} 設定がある場合は、各ターゲットの binaries API に移動します。isStatic = true を推奨します。動的フレームワークは SwiftPM のインポートに関する既知の例外ケースがあり、リンカーエラー、dyld クラッシュ、またはクラスの重複警告が発生する可能性があります。

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

cocoapods.framework {} ブロックに export(project(...)) または transitiveExport = true が含まれていた場合は、新しい binaries.framework {} ブロックにも引き継いでください。これらは、フレームワークが子モジュールをエクスポートするマルチモジュールプロジェクトで不可欠です。

3.4 dev.gitlive/firebase-kotlin-sdk および CocoaPods 時代の同様の KMP ラッパーを扱う

プロジェクトで dev.gitlive:firebase-* または同様の KMP ラッパーライブラリを使用している場合は、追加で次の 2 つの手順が必要です。

A. isStatic = true に切り替える — 動的フレームワークと Firebase SPM の組み合わせでは、実行時に dyld クラッシュが発生します。切り替え後、integrateLinkagePackage を再実行し、「Embed Frameworks」コピー フェーズを削除して、リンカーフラグを OTHER_LDFLAGS に移動します。

B. フレームワークの検索パスを追加する — build.gradle.kts に条件付きの -F linkerOpts を追加し、Xcode プロジェクトにも対応する FRAMEWORK_SEARCH_PATHS を追加します。

コード例と製品の完全な一覧については、common-pods-mapping.md § dev.gitlive および troubleshooting.md を参照してください。

3.5 オプトインアノテーションを追加する

swiftPackage() および localSwiftPackage() DSL 関数には @ExperimentalKotlinGradlePluginApi が付与されています。コンパイラーの警告を抑制するには、このオプトインを追加します。

@file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)

swiftPackage() または localSwiftPackage() を呼び出す各 build.gradle.kts ファイルの先頭に記述してください。

Kotlin ソースファイルには、cinterop のオプトインも追加してください。

kotlin.compilerOptions {
    optIn.add("kotlinx.cinterop.ExperimentalForeignApi")
}

DSL の詳細なリファレンスについては、dsl-reference.md を参照してください。


フェーズ 4:Kotlin ソースの更新

インポート名前空間の計算式
swiftPMImport.<group>.<module>.<ClassName>

Where:
- group: build.gradle.kts `group` property of the MODULE THAT DECLARES the swiftPMDependencies, dashes (-) → dots (.)
- module: Gradle module name of the MODULE THAT DECLARES the swiftPMDependencies, dashes (-) → dots (.), underscores (_) preserved as-is
- ClassName: Objective-C class name (FIR* for Firebase, GMS* for Google Maps)

名前空間には、インポート元モジュールではなく、宣言側モジュールの group と name を使用します。 これはエージェントが最もよく間違える点です。モジュール A がモジュール B に依存し、モジュール B が swiftPMDependencies を宣言している場合、モジュール A はモジュール B の group とモジュール名を使って SPM クラスをインポートします。モジュール A のものではありません。

変換例 — 単一モジュール
// 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
変換例 — マルチモジュール(linkOnly ポッド)

子モジュールが cinterop バインディングを提供するため、利用側モジュールで pod("GoogleMaps") { linkOnly = true } を指定していた場合:

// composeApp/App.kt — composeApp depends on :google-maps Gradle module
// google-maps has group = "org.jetbrains.kotlin.google-maps", module name = "google-maps"
// google-maps declares swiftPMDependencies with GoogleMaps

// BEFORE (in composeApp):
import cocoapods.GoogleMaps.GMSServices

// AFTER — uses google-maps module's namespace, NOT composeApp's namespace:
import swiftPMImport.org.jetbrains.kotlin.google.maps.google.maps.GMSServices
//                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^
//                    google-maps's group (dashes→dots)  google-maps's module name (dashes→dots)

インポートパスでは composeApp の group(org.jetbrains.kotlin.compose.sample)を使用しません。cinterop バインディングが生成される場所である、宣言側モジュールの識別情報を使用します。

インポートのフラット化: Clang モジュール名(例:FirebaseFirestoreInternal、FirebaseAuth)はインポートパスからなくなり、元のライブラリに関係なく、すべてのクラスが同じ swiftPMImport.<group>.<module> プレフィックスの下にフラット化されます。たとえば、cocoapods.FirebaseAuth.FIRAuth と cocoapods.FirebaseFirestoreInternal.FIRFirestore は、それぞれ swiftPMImport.<group>.<module>.FIRAuth と swiftPMImport.<group>.<module>.FIRFirestore になります。

同梱 Klib のインポートを保持する

重要: サードパーティの KMP ライブラリに同梱された cinterop klib に解決される cocoapods.* インポート(フェーズ 1 の手順 1.3 で特定)を置き換えないでください。これらのインポートはそのままにする必要があります。cocoapods プレフィックスは、ライブラリの公開済み klib におけるパッケージ名前空間であり、実際の CocoaPods 依存関係ではありません。swiftPMDependencies の cinterop ジェネレーターは、依存関係の klib ですでに提供されているモジュールをスキップするため、これらのクラスに対する swiftPMImport.* は「Unresolved reference」エラーになります。

例(KMPNotifier を使用するプロジェクト):

// KEEP — resolves to kmpnotifier's bundled cinterop klib
import cocoapods.FirebaseMessaging.FIRMessaging
一括置換

フェーズ 1 の手順 1.3 で特定したインポートを除外し、すべての Kotlin ソースファイルを対象に正規表現で検索・置換します。マルチモジュールプロジェクトでは、各モジュールの group と name を使って、モジュールごとに置換を実行します。

Find:    cocoapods\.\w+\.
Replace: swiftPMImport.<declaring.module.group>.<declaring.module.name>.

一括置換後、保持する必要のある cocoapods.* インポート(同梱 klib 由来)を手動で元に戻します。

正しいインポートパスの確認方法: ./gradlew :moduleName:compileKotlinIosSimulatorArm64 を実行します。エラーに利用可能なクラスが表示されます。


フェーズ 5:iOS プロジェクトの再構成

5.1 マイグレーションコマンドの取得

CocoaPods ワークスペースをビルドして、マイグレーションコマンドを取得します:

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'

ビルド出力には、次のようなコマンドが含まれます:

XCODEPROJ_PATH='/path/to/project/iosApp.xcodeproj' GRADLE_PROJECT_PATH=':shared' '/path/to/project/gradlew' -p '/path/to/project' ':shared:integrateEmbedAndSign' ':shared:integrateLinkagePackage'

このコマンドを実行します。これにより .xcodeproj が変更され、ビルド中に embedAndSignAppleFrameworkForXcode がトリガーされるようになります。integrateLinkagePackage は一度だけ行うセットアップであり、ビルドフェーズとして追加する必要はありません。integrateEmbedAndSign がスキップされた場合は、EmbedAndSign を無効化する設定(フェーズ 1 のステップ 1.2)がないか確認してください。まずそれらを削除してから、再実行します。

embedAndSignAppleFrameworkForXcode が有効であることを確認する: 統合を実行した後、project.pbxproj 内のビルドフェーズスクリプトを確認してください。embedAndSignAppleFrameworkForXcode がコメントアウトされている場合(# が先頭に付いている場合)は、コメントを解除してください。

integrateLinkagePackage タスクは、<iosDir>/ に KotlinMultiplatformLinkedPackage/ を生成します。これは、products リストを反映し、SPM ライブラリが最終バイナリにリンクされることを保証するローカルの Swift パッケージです。

統合タスクを実行した後は、.xcodeproj 内で User Script Sandboxing を無効化(ENABLE_USER_SCRIPT_SANDBOXING = NO)してください。Xcode 16 以降ではこれがデフォルトで有効になっており、Gradle のビルドフェーズがプロジェクトディレクトリに書き込むことを妨げます:

sed -i '' 's/ENABLE_USER_SCRIPT_SANDBOXING = YES/ENABLE_USER_SCRIPT_SANDBOXING = NO/g' "$XCODEPROJ_PATH/project.pbxproj"

この設定が存在しない場合(Xcode のデフォルトは YES)、アプリターゲットの buildSettings セクションに ENABLE_USER_SCRIPT_SANDBOXING = NO; を追加してください。その後、Gradle デーモンを再起動します:./gradlew --stop

代替手段(xcodebuild のアプローチが失敗する場合): パスを検出して統合タスクを直接実行するためのフォールバックスクリプトについては、troubleshooting.md の「Manual Integration Command Discovery」のセクションを参照してください。

5.2 Crashlytics dSYM アップロードスクリプトの更新(該当する場合)

プロジェクトで FirebaseCrashlytics を使用しており、dSYM アップロードの実行スクリプトフェーズがある場合(フェーズ 1 のステップ 11 で特定)、スクリプトのパスを ${PODS_ROOT}/FirebaseCrashlytics/upload-symbols から "${BUILD_DIR%/Build/*}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run" に更新してください。スクリプト全体と入力ファイルのリストについては、troubleshooting.md の「Firebase Crashlytics: dSYM Upload Script」および common-pods-mapping.md を参照してください。

5.3 CocoaPods の統合解除

オプション A:完全な統合解除(CocoaPods が KMP 依存関係のみに使用されていた場合):

ファイルを削除する前に、git status --short を実行してパスを確認してください。不明な場合は、すぐに削除するのではなく、ファイルをバックアップ場所に移動してください。

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

このクリーンアップスニペットは自己完結型であり、先ほどの一度限りのマイグレーションコマンドの XCODEPROJ_PATH や GRADLE_PROJECT_PATH がシェル内でまだ利用可能であることを前提としていません。

pod deintegrate が利用できない場合は、削除すべき参照の完全なリストについて、troubleshooting.md の「Manual CocoaPods Deintegration from pbxproj」のセクションを参照してください。また、.gitignore から Pods/ を削除し、.xcworkspace ディレクトリを削除してください。

オプション B:部分的な削除(KMP 以外の CocoaPods 依存関係が残っている場合):

Podfile から KMP の pod 行のみを削除し、pod install を再実行します:

target 'iosApp' do
  # Remove this line:
  pod 'shared', :path => '../shared'
  # Keep other non-KMP pods
end
cd /path/to/iosApp && pod install

Tip: 残りの pod も SPM へ移行することを検討してください。ほとんどの人気のある iOS ライブラリは SPM をネイティブにサポートしています。Xcode で File → Add Package Dependencies から追加し、すべての pod を置き換えたら CocoaPods を完全に統合解除してください。

5.4 手動統合(自動統合が失敗する場合)

5 ステップの手動セットアップ(ビルドフェーズ、サンドボックス、リンケージパッケージ)については、troubleshooting.md の「Manual Xcode Integration Steps」のセクションを参照してください。


フェーズ 6: Gradle から CocoaPods を削除

iOS プロジェクトの再設定が完了したので、CocoaPods プラグインとブロックを、それを使用していたすべてのモジュール(主要なものだけでなく)から削除します:

6.1 CocoaPods プラグインの削除

それを使用していたすべてのモジュールの plugins {} から kotlin("native.cocoapods") または alias(libs.plugins.kotlinCocoapods) を削除します:

plugins {
    // REMOVE: kotlin("native.cocoapods")  or  alias(libs.plugins.kotlinCocoapods)
    alias(libs.plugins.kotlinMultiplatform)  // Keep
}

すべてのモジュールがマイグレーション済みの場合は、gradle/libs.versions.toml から kotlinCocoapods プラグインのエントリも削除します:

[plugins]
# REMOVE: kotlinCocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" }
6.2 cocoapods ブロックを削除する

build.gradle.kts から cocoapods { ... } ブロック全体を削除します。Phase 3 で追加した swiftPMDependencies {} ブロックと binaries.framework {} 設定がその代わりとなります。また、モジュールディレクトリから生成された .podspec ファイル(例:shared/shared.podspec)も削除してください。これらは CocoaPods プラグインによって生成されたもので、今後は不要です。

6.3 非推奨の gradle.properties エントリを削除する

Phase 1.1 で見つかった場合は、gradle.properties から削除します。

# REMOVE — no longer needed after migrating away from CocoaPods (KT-64096)
kotlin.apple.deprecated.allowUsingEmbedAndSignWithCocoaPodsDependencies=true
6.4 CocoaPods 関連の追加項目を整理する

Phase 1 の手順 12 で特定した追加項目を確認します。Podspec のメタデータ、noPodspec()、CocoaPods タスクフック、Pods.xcodeproj のパッチ適用コードは、ユーザーに確認せずに安全に削除できます。標準外の Pod 設定(extraOpts、moduleName)、カスタム cinterop の defFile 設定、CocoaPods 固有のコンパイラ/リンカーフラグは、分析が必要です。SPM が自動的に処理するかどうか不明な場合は、ユーザーに確認してください。

例を含むカテゴリ別の一覧については、cocoapods-extras-patterns.md を参照してください。


Phase 7: 検証

アプリケーションのビルドが正常に完了するまで、作業を終了しないでください。 このフェーズは反復的に進めます。いずれかの手順で失敗した場合は、エラーを診断して修正し(troubleshooting.md を参照し、Phase 2~6 を再確認してください)、失敗した手順を再実行します。ビルドが成功するまで、または問題が明らかに移行の範囲外(既存のバグ、無関係なツールの問題)であると判断できるまで繰り返してください。ビルドが成功するまでは、移行レポート(Phase 8)を作成しないでください。

7.1 Kotlin コードをコンパイルする

移行したモジュールをコンパイルし、Kotlin ソースが正しいことを確認します。

./gradlew :moduleName:compileKotlinIosSimulatorArm64

コンパイル時に未解決の参照エラーが発生した場合は、インポートの変換(Phase 4)と SwiftPM の依存関係宣言(Phase 3.2)を確認してください。よくある原因として、importedClangModules の不足、Clang モジュール名の誤り、変換すべきなのに残されたバンドル済み klib のインポート(またはその逆)があります。

7.2 フレームワークをリンクする
./gradlew :moduleName:linkDebugFrameworkIosSimulatorArm64

リンクに失敗した場合は、必要な SPM プロダクトがすべて宣言されていることと、バージョン制約が正しく解決されることを確認してください。シンボルが見つからないというリンクエラーは、多くの場合、swiftPMDependencies からプロダクトが漏れているか、バージョンの不一致によって API が削除されたことを示しています。

7.3 iOS/macOS の Xcode プロジェクトをビルドする

Gradle の手順が成功したら、Xcode プロジェクトをビルドし、アプリケーション全体をコンパイルできることを確認します。CocoaPods をすべて削除した場合(オプション A)は -project *.xcodeproj を使用し、KMP 以外の CocoaPods が残っている場合(オプション B)は -workspace *.xcworkspace を使用します。

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 build

checkSandboxAndWriteProtection が失敗した場合 — Phase 5.1 でサンドボックス化が無効になっていません。戻って Phase 5.1 のサンドボックス修正を適用し、再試行してください。

移行前のビルドが検証されていない場合(Phase 1.0 のフォールバックを使用した場合)は、ユーザーに警告します。

注:移行前のビルドを完全には検証できませんでした。現在ビルドエラーが発生した場合、その一部は移行とは無関係な既存の問題である可能性があります。エラーを移行前のビルド出力と比較し、移行による問題と以前からの問題を区別してください。

ビルドに失敗した場合

移行を元に戻さないでください。 エラーログを読み、Phase 2~6 を再確認し、troubleshooting.md を参照してください。判断に迷う場合は、ユーザーに選択肢を提示してください。移行作業を黙って取り消してはいけません。問題を修正し、失敗した検証手順を再実行してください。ビルドが成功するまで反復を続けてください。


フェーズ 8:移行レポート

ビルドが成功したら、プロジェクトのルートに包括的な MIGRATION_REPORT.md を作成します。migration-report-template.md のテンプレートを使用してください。

レポートには、次の内容を含める必要があります。

  1. 移行前の状態 — CocoaPods の依存関係(名前、バージョン、linkOnly)、フレームワークの設定、cocoapods.* のインポート、KMP 以外の Pod、一般的でない設定
  2. 移行手順 — 各フェーズでの変更内容を正確に記載し、重要な変更については変更前/変更後のスニペットを示す
  3. インポートの変換 — すべてのインポート変更の表。保持された cocoapods.* のインポートと、それらを提供するバンドル済み klib を明記する
  4. 発生したエラー — 構造化された Error #N エントリ:フェーズ、正確な症状、根本原因、修正方法、一般化可能かどうか
  5. 重要な判断 — isStatic の変更、保持したインポート、フレームワークの検索パス、トレードオフ
  6. 変更したファイル — 種類別に分類した完全な一覧(Gradle、Kotlin、Xcode、作成、削除)

追加リソース

コメント