Claude CodeでKotlin Multiplatformを使う — 共有コードの書き方とiOS統合
Claude CodeでKotlin Multiplatformを書く際の前提条件、CLAUDE.mdでのソースセット別ルール、Gradleでのビルド・テスト、iOS統合の制約をまとめます。
要点
Claude CodeでKotlin Multiplatform(KMP)のプロジェクトを扱うと、プラットフォーム専用APIを共有コードに書いてビルドを壊す提案が返ってくることがあります。原因はClaude自体の実力不足ではなく、commonMain と androidMain / iosMain の境界をプロジェクト側が伝えていないことです。
この記事では、KMPプロジェクトの前提条件とセットアップの選択肢、CLAUDE.mdと .claude/rules/ でソースセットの境界を教える方法、Gradleコマンドでのビルド・テストの回し方、iOS統合で外せないmacOS/Xcodeの制約までを順番に扱います。Claude Code自体のインストールや基本操作はClaude Codeとは何かをまとめた記事にまとまっています。
KMPプロジェクトの前提とセットアップの選択肢
Kotlin Multiplatformは、Android・iOS・デスクトップ・Web・サーバー間でコードを共有するJetBrainsのオープンソース技術です。ロジックだけを共有してUIはネイティブのままにする構成も、Compose MultiplatformでUIごと共有する構成も選べます。
公式のセットアップ手順は基本的にIDE前提です。IntelliJ IDEA 2025.2.2以降かAndroid Studio Otter 2025.2.1以降にKotlin Multiplatform IDEプラグインを入れ、新規プロジェクトウィザードでAndroid・iOS・デスクトップなどのターゲットを選んで生成します。iOSターゲットを含める場合はmacOSホストにXcodeが必要です。IDEはビルドの裏側でXcodeを呼び出すため、初回起動を済ませて初期セットアップを終わらせておく必要があります。
IDEのウィザードを開かずに始めたい場合は、JetBrainsが公開しているKotlin Multiplatform WizardというWebツールが使えます。プロジェクト名とターゲット(Android・iOS・デスクトップ・Web・サーバー)を選ぶと、Gradleプロジェクトの雛形がzipでダウンロードできます。IDEを開かず、ターミナルとClaude Codeだけで作業を始められる点が実用的です。ビルドシステムはGradleと、JetBrainsが「AI-friendly」と説明する新しいKotlin Toolchainのどちらかを選べますが、既存のドキュメントやサンプルコードの大半はGradle前提です。Claude Codeに読ませる参考情報の量を考えると、当面はGradleを選ぶ構成のほうが手堅い選択です。
ANDROID_HOME 環境変数の設定も欠かせません。.profile や .zprofile に次の1行を追記しておくと、Claude Codeがターミナルから叩くGradleタスクも同じ環境変数を参照します。
export ANDROID_HOME=~/Library/Android/sdkCLAUDE.mdと.claude/rules/でソースセットの境界を教える
KMPプロジェクトの最大の特徴は、同じKotlinの構文でも書ける場所によって使えるAPIが変わることです。commonMain は宣言した全ターゲットに向けてコンパイルされるため、java.io.File のようなJVM専用APIや platform.Foundation.NSUUID のようなiOS専用APIを直接使うとコンパイルエラーになります。プラットフォーム固有の処理は、ターゲットごとの専用ソースセット(androidMain・iosMain など)に書き分ける必要があります。
Claude Codeはこの境界をプロジェクトの外から推測できません。プロジェクト直下のCLAUDE.mdに大枠のルールを書き、モジュールごとの細かい注意点は .claude/rules/ にパス指定で分けると、該当ファイルを開いたときだけ読み込まれるため文脈を圧迫しません。ルールファイルはYAMLフロントマターの paths フィールドにglobパターンを書くことでスコープを絞れます。
---
paths:
- "shared/src/commonMain/**/*.kt"
---
# commonMainで守ること
- java.io.File・android.*・platform.*などプラットフォーム専用APIを
直接importしない
- プラットフォーム固有の実装が要る場合はexpect宣言を追加し、
androidMain・iosMainの両方にactual宣言を書く
- ビルド確認はテストが通る範囲でallTestsタスクを使うcommonMainで書ける範囲とexpect/actual宣言
expect/actual宣言とは、共通コードから見えるAPIの形だけを宣言し、実装は各プラットフォームのソースセットに任せる仕組みです。commonMain で expect を付けた関数・クラス・プロパティを宣言し、androidMain や iosMain など各ソースセットで同じシグネチャに actual を付けて実装します。コンパイラは両者をターゲットごとに1つの宣言へマージし、パッケージが一致しない宣言や実装が欠けたターゲットがあればエラーにします。
公式ドキュメントは commonMain ・jvmMain ・nativeMain の3ソースセットを例に説明していますが、Android/iOS構成でも考え方は同じで、commonMain ・androidMain ・iosMain に置き換えて読めます。
package identity
class Identity(val userName: String, val processID: Long)
expect fun buildIdentity(): Identitypackage identity
actual fun buildIdentity() = Identity(
System.getProperty("user.name") ?: "None",
ProcessHandle.current().pid()
)Claude Codeにこの型のコードを書かせる際は、「expect を追加したら、対応する全ソースセットに actual を用意する」という一往復をタスクの単位にすると、片方だけ実装して残りのターゲットがビルド不能になる状態を避けやすくなります。IDEはこのペアをガター(行番号の脇)アイコンで相互ジャンプできるように可視化しますが、Claude Codeはターミナル越しの作業なので、この確認はビルドかテストの実行結果で代替します。
Gradleでビルド・テストをClaude Codeに任せる
Claude Codeが実行できる範囲はターミナルコマンドに限られるため、IDEのRunボタンの代わりにGradleタスクを直接叩く形になります。テストはIDEのガターアイコンからだけでなく、allTests タスクで全ターゲットをまとめて実行できます。Androidは testDebugUnitTest と testReleaseUnitTest、iOSシミュレーターは iosSimulatorArm64Test のように、対象ターゲット名に Test を付けたタスクで個別に走らせることもできます。
./gradlew allTests実行結果は build/reports/tests 配下にHTMLレポートとして出力されますが、Claude Codeが判断に使うのはターミナルの終了コードと標準出力です。テストの合否をClaudeが自分で読める状態にしてから実装を任せる進め方は、Claude CodeでTDDを回す手順で扱っているループと同じ要領で回せます。
毎回の実行確認をスキップしたい場合は、.claude/settings.json の許可リストにGradleコマンドを追加しておきます。
{
"permissions": {
"allow": [
"Bash(./gradlew allTests)",
"Bash(./gradlew *Test)"
]
}
}androidMain 側と iosMain 側のプラットフォーム実装を並行して進めたい場合は、Claude Codeのサブエージェントでモジュールごとに調査・実装を分担させる方法も有効です。片方の実装がXcodeのビルド待ちで止まっている間に、もう一方のサブエージェントにAndroid側の実装を進めさせられます。
iOS統合とmacOS環境の制約
共有モジュールをiOSアプリに組み込む方法は、大きく分けてローカル統合とリモート統合の2種類です。ローカル統合には、Xcodeのビルドフェーズにスクリプトを追加する直接統合(Kotlin Multiplatform IDEプラグインを使う場合のデフォルト)、ローカルのSwiftパッケージを介した統合、CocoaPodsを介した統合があります。リモート統合では、XCFrameworkをSwiftPMまたはCocoaPods経由でサードパーティ依存のように配布します。
どの方法を選んでも、iOSフレームワークのビルドそのものはmacOS上のXcodeが担います。Linuxコンテナの中では完結しません。Claude CodeをGitHub Codespacesで動かす場合、公式のDev Container Featureが前提にしているベースイメージはUbuntuです。Android側のGradleタスクはこの環境でも走りますが、Xcodeを呼ぶiOS側のビルドはローカルのmacOS機に残ります。
作業をClaude Codeに任せやすいかどうかを整理すると、次のようになります。
| 作業 | Claude Codeへの委任 | 理由 |
|---|---|---|
| commonMainのロジック実装・テスト | Claude Codeへの委任◎ | 理由allTestsの結果をClaudeが直接読める |
| Androidターゲットのビルド・テスト | Claude Codeへの委任◎ | 理由./gradlewだけで完結する |
| iOSフレームワークのビルド | Claude Codeへの委任△ | 理由macOS+Xcodeのローカル環境が前提 |
| Xcode側のUI実装・シミュレーター操作 | Claude Codeへの委任✕ | 理由GUI操作が中心でCLIから完結しない |
よくあるつまずき
commonMainへのプラットフォームAPI混入は、実装を急ぐほど起きやすいつまずきです。androidMain で使えていたクラスをそのまま commonMain に移した瞬間にビルドが壊れます。.claude/rules/ でこの境界を明示しておくと、Claudeが提案する時点で気づける確率が上がります。
expect宣言の実装漏れも同種のつまずきです。expect を追加したのに一部のターゲットで actual を書き忘れると、そのターゲットのビルドだけが失敗します。共通コードの変更と各プラットフォームの実装をひとまとめのタスクにして、allTests で全ターゲットの結果を確認する習慣が有効です。
JetBrainsの公式ドキュメントは、KMP向けのAIコーディングエージェントとして自社の「Junie」を案内しています。Claude CodeはKMPの公式統合ではなく汎用のCLIエージェントとして使う形になるため、IDE内のAI機能に関する記述の一部はJunie前提で書かれている場合がある、という点は理解しておくとよいでしょう。
共有ロジックにMCPサーバー機能を持たせたい場合は注意が必要です。MCP公式SDKのKotlin実装は成熟度の低いTierに位置づけられており、Swift・PHPと同様に機能が発展途上です。
まとめ
Claude CodeでKMPプロジェクトを書く際は、ソースセットの境界とexpect/actualの対応関係をCLAUDE.mdと .claude/rules/ で教え、Gradleタスクを許可リストに登録してビルド・テストのループを自己完結させる構成が現実的です。iOSフレームワークのビルドだけはmacOS+Xcodeのローカル環境に残るため、Android側の実装やCIをLinux環境に寄せつつ、iOS側だけローカルで最終確認する分担が扱いやすくなります。