kmp-boundaries について
Kotlin Multiplatformで共有コードとプラットフォーム実装の境界を設計するスキルです。機能別インターフェース、Activityが所有するAndroid UIバインディング、AGP 9のKMPライブラリ制約を扱います。
主な機能
- 01意味を表すcommonMainインターフェース
- 02機能別のプラットフォーム境界
- 03ActivityスコープのAndroid UIバインディング
- 04AGP 9 KMPライブラリの制約
- 05iOS–Swift相互運用のリファレンス
活用シーン
- 01プラットフォーム機能の共有API設計
- 02AndroidバインディングのActivityライフサイクル漏れ防止
- 03AndroidアプリのAGP 9 KMPライブラリへの移行
- 04Swift呼び出し向けKotlin APIの準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rcosteira79/ android-skills --skill kmp-boundariesインストール後、Claude Code で/kmp-boundariesを入力して読み込まれたか確認します。
同梱ファイルreferences/ios-interop.md
詳細
Kotlin Multiplatform の境界設計
KMP の境界に関する基本ルール:
commonMainはセマンティックに保つ — Android/iOS の仕組みではなく、プロダクトが必要とする 内容 を表します。currentRegion()とし、currentRegionFromAndroidLocale(context)とはしません。- 機能ごとに分割する — 1 つの巨大な
Platformオブジェクトではなく、Clipboard、ShareSheet、Haptics、Biometricsを個別のインターフェースにします。 - actual は薄く保つ — actual の役割は変換であり、判断ではありません。actual 内にビジネスロジックの
if/whenがある場合は、それを common に置き、フェイクでテストします。 - フェイク/DI/ライフサイクル/実行時の選択が必要な場合は、
expect classよりも common のinterfaceとプラットフォームごとのバインディングを優先します。 - 中間ソースセット(
skikoMain、appleMain)を導入するのは、2 つのプラットフォームで実際の実装を本当に共有する場合に限ります。
以下では、特に詳しく扱う境界を 2 つ紹介します。Activity が所有するプラットフォーム UI 境界と、AGP 9 の KMP ライブラリに関する制約です。
関連項目: android-skills:kmp-ktor(ネットワーク境界)、compose/references/multiplatform.md(Compose-MP の仕組み)、android-skills:kotlin-coroutines(スコープの所有権)。iOS↔Swift ブリッジ(Kotlin→Swift の命名、型幅(Int は 32 ビット)、SKIE による suspend→async/Flow→AsyncSequence、sealed クラスの網羅性、SwiftUI への埋め込み)については、iOS 側の actual を作成する際に references/ios-interop.md を読み込んでください。
プラットフォーム UI バインディングは Context ではなく Activity が所有する
Android の境界における最もよくある間違いは、実際には Activity が必要なバインディングに applicationContext/LocalContext.current を渡し、その後 Intent.FLAG_ACTIVITY_NEW_TASK でライフサイクル上の不足を取り繕うことです。このフラグは要注意です。フォアグラウンド UI の操作であることを隠してしまいます。代わりに Activity を保持してください。
// commonMain — semantic interface; DOCUMENT what `suspend` means
interface ShareSheet {
/** Launches the system share sheet. Returns when the sheet is PRESENTED — not when the user
* completes or cancels. (Otherwise callers write incorrect retry/confirmation logic.) */
suspend fun shareText(text: String)
}
// androidMain — thin: build the intent and launch it. Activity-owned.
class AndroidShareSheet(private val activity: Activity) : ShareSheet {
override suspend fun shareText(text: String) {
activity.startActivity(Intent.createChooser(
Intent(Intent.ACTION_SEND).setType("text/plain").putExtra(Intent.EXTRA_TEXT, text), null,
))
}
}
Activity をアプリ全体に注入することはしません(Activity はフレームワークが作成し、ライフサイクルに紐づいているためです)。Android アプリモジュールの アクティビティスコープ でバインディングを構築します(Hilt の場合は @InstallIn(ActivityComponent::class)。Activity はデフォルトのバインディングです。Koin の場合は scoped)。commonMain から見えるのはインターフェースだけであり、Activity がアプリモジュールの外に出ることはありません。より長いライフサイクル(アプリスコープ)を持つオブジェクトが Activity を必要とする場合は、ライフサイクルを認識するプロバイダーの背後に保持してください(onResume で設定し、onPause でクリアします)。これにより、破棄された Activity のリークを防げます。
AGP-9 KMP ライブラリの制約(構造上の制約 — 共有コードに含められるものを左右します)
AGP 9 では、KMP モジュールの Android 側で com.android.library が com.android.kotlin.multiplatform.library に置き換わり、com.android.application と kotlin.multiplatform の併用は明確に拒否されます。新しいプラグインでは、単一バリアントのアーキテクチャが強制されます。
BuildConfigは使用できません — コンパイル時定数には、BuildKonfig または注入されたAppConfigurationインターフェースを使用します。BuildConfig.Xが存在することを前提としたcommonMainAPI は設計しないでください。- ビルドバリアントはありません — バリアント固有の依存関係、リソース、署名設定はアプリモジュールに置きます。デバッグ版かリリース版かの判断は、KMP モジュール内でビルドバリアントを分けるのではなく、共有コードに注入する実行時設定値として渡します。
- NDK / JNI は使用できません — ネイティブコード(C/C++)は別の
com.android.libraryモジュールに切り出し、KMP モジュールが利用する共通インターフェースの背後にラップします。 - Compose-MP リソースは明示的に有効化する必要があります —
kotlin { android { … } }内にandroidResources { enable = true }を追加してください。追加しないと、Android で実行時にRes.string.*/Res.drawable.*がクラッシュします(ビルド自体は成功するため、見落としやすい点です)。 - コンシューマー向け ProGuard ルールの移行が必要です — 古い
android {}ブロックのconsumerProguardFiles("rules.pro")は通知なしに無視されます。新しい DSL ではconsumerProguardFiles.add(file("rules.pro"))を使用してください。 - KMP モジュールを
com.android.applicationとして使うことはできません — Android のエントリーポイント(MainActivity、Application クラス、ランチャーマニフェスト、applicationId/targetSdk/versionCode/versionName)は、共有ライブラリに依存する別のandroidAppモジュールに移します。MainActivity、アプリレベルの Hilt 設定、ナビゲーションホストの配線はすべて、共有モジュールのandroidMainから移動します。 - kapt は AGP 9 の組み込み Kotlin と互換性がありません — アノテーションプロセッサを KSP(2.3.1 以降)に移行するか、KSP に相当するものがないプロセッサでは
com.android.legacy-kaptに切り替えます。
| 懸念事項 | AGP 9 より前(モノリシック) | AGP 9 の KMP ライブラリ |
|---|---|---|
MainActivity、Application クラス、ランチャーマニフェスト | 共有モジュールの androidMain | 別の androidApp モジュール |
applicationId、versionCode、targetSdk | 共有モジュールの android {} | androidApp のみ |
| コンパイル時定数(環境、フラグ) | BuildConfig フィールド | common の BuildKonfig、または実行時 DI |
| NDK / JNI のネイティブコード | androidMain(任意のモジュール) | 共通インターフェースを介した別の com.android.library |
既存のプロジェクトを移行する場合は、具体的な手順について JetBrains の kotlin-tooling-agp9-migration スキルを参照してください。