はじめに
新規事業でネイティブアプリを開発をすることになり、KMPを採用したクロスプラットフォーム開発をおこなうことになったのでKMPとは何なのか?Flutterと比較して何が違うのか?などをキャッチアップしてまとめました。
KMPって何?
「共通のビジネスロジックをKotlinで書き、UIは各プラットフォーム(iOS, Android)のネイティブ機能を活かして実装する」という開発手法
すべてを共通化するのではなくロジックは共通、UIはネイティブで実装するのが特徴
- 共通化するもの:データの計算、API通信、認証ロジック...
- 共通化しないもの:UI、カメラ、通知などのOS固有の機能...
UIを共通化するCompose Multiplatform(CMP)もある
KMPはビジネスロジックに焦点を当てて共通化する技術だが、CMPを導入することでUIまで共通化できる。
ロジック共通化 × ネイティブUIによるメリット
1. ネイティブと同等の高品質なパフォーマンス
- KMPでは他のクロスプラットフォーム技術と異なり、UI層を強制的に共通化しない
- JavaScriptブリッジや独自の描画エンジンを介さないため、アプリの起動速度、メモリ使用量、バイナリサイズに置いてネイティブアプリと遜色ない性能を発揮する
2. ネイティブアプリ同等のUXの維持
- UIを各プラットフォームの標準ツール(SwiftUIやJetpack Compose)で構築することで、OS固有の最新機能やデザインの作法をそのまま活用できる
- スクロールの感触、ダークモード、アクセシビリティ対応など、ユーザーが慣れ親しんだ各OS特有のUXを完璧に提供できる
3. 開発効率と保守性の向上
- ビジネスロジックを一元化することで、「アプリの頭脳」部分の重複開発をなくし、品質を安定させることが可能
- バグの削減: ネットワーク通信、データベース操作、バリデーションロジックなどを一度の実装で済ませるため、プラットフォーム間での挙動の不一致を防ぎ、修正も一箇所で完了できる
Flutter、ReactNativeと比較して何が違う?

どのフレームワークが優れている、劣っているというわけではなく、事業規模やエンジニアのリソースなどによって選定するフレームワークの最適解は変わってきます。
KMPに限ってまとめると以下のような場合に採用するメリットがありそうです
- Android, Kotlin経験者が多く、知見や資産がある場合
- iOSエンジニアがSwiftUIに習熟しており、UIのネイティブ感に妥協せず、効率的に開発をしたい場合
- 共通ロジック、個別に作り込むUIを明確に分離して、長期的なメンテコストを抑えたい場合
どう設計するのがベストか?
KMPプロジェクトにおける設計はざっくり、以下の画像のような感じです。
基本的にはビジネスロジックは各プラットフォームで共通の処理となるが、カメラ,センサー,特定のSDK,ファイルパスを扱う場合は各プラットフォームごとに実装をする必要がある。
expect/actual構文
プラットフォームごとの実装を行う場合は以下のように実装をすることで分岐させることができます。
1.【commonMain】にinterfaceとexpectを定義
まずは共通コード側で【何ができるか】を定義します
// 1. インターフェースを定義(OSごとの振る舞いの抽象化)
interface DeviceInfo {
val name: String
}
// 2. インターフェースを返す関数を expect で宣言
expect fun getDeviceInfo(): DeviceInfo
2.【androidMain】にactualを実装
次にAndroid側のソースで、具体的な処理を実装します
// 3. インターフェースの実装クラスを作成
class AndroidDeviceInfo : DeviceInfo {
override val name: String = "Android ${android.os.Build.MODEL}"
}
// 4. expect に対する actual 関数の実装
actual fun getDeviceInfo(): DeviceInfo = AndroidDeviceInfo()
3.【iosMain】にactualを実装
同様にiOS側でもSwiftのAPIを使用して実装します
import platform.UIKit.UIDevice
// 3. インターフェースの実装クラスを作成
class IosDeviceInfo : DeviceInfo {
override val name: String = "${UIDevice.currentDevice.systemName} ${UIDevice.currentDevice.systemVersion}"
}
// 4. expect に対する actual 関数の実装
actual fun getDeviceInfo(): DeviceInfo = IosDeviceInfo()
4.ビジネスロジック側で呼び出す
このように共通の処理をinterfaceとして抽象化して、それぞれのプラットフォームごとに具体を実装してあげることで、ビジネスロジック側ではどの処理を扱うのかを意識せず呼び出すことができます。
// commonMain
class GreetingService {
fun greet(): String {
val info = getDeviceInfo() // ここで共通コードとして呼び出せる
return "Hello from ${info.name}!"
}
}
使えるライブラリ
KMPを導入してクロスプラットフォームで開発をする場合にライブラリがKMPに対応しているのかが気になるところですが、メジャーなものはアップデートにより対応しているものが増えてきています。
| カテゴリ | Androidでの標準 | KMPでの定番(2026年時点) | KMP対応状況 |
|---|---|---|---|
| ネットワーク | Retrofit / OkHttp | Ktor | ✅ 完全対応 |
| シリアライズ | Moshi / Gson | kotlinx.serialization | ✅ 公式・標準 |
| データベース | Room | Room / SQLDelight | ✅ RoomもKMP対応済み |
| 画像読み込み | Glide / Coil | Coil (v3〜) | ✅ KMP対応版が登場 |
| 非同期処理 | Coroutines / Flow | Coroutines / Flow | ✅ そのまま利用可能 |
| DI(依存注入) | Hilt / Dagger | Koin / Kodein | ⚠️ HiltはAndroid専用 |
| UI状態管理 | ViewModel | Lifecycle ViewModel | ✅ Jetpack公式がKMP化 |
| ナビゲーション | Jetpack Navigation | Navigation Compose | ✅ KMP対応済み |
2026年1月現在、さまざまなライブラリのアップデートが行われ、KMPだけでなくCMPの対応も進んでいるので今後、採用する際の懸念点も少なくなってきそうです。
万が一、使用したいライブラリがKMPに対応していない場合は前述のexpect/actualを使用して実装を行います。
さいごに
私自身、会社内で導入前例がない技術で新規開発を行うのが初めてなのでキャッチアップに苦戦しました。
特にKMP, CMPでの導入事例が他社でもあまりなかったためFlutterやReactNativeに比べると情報があまりなかったので公式ドキュメントの情報を活用しながら進めました。NotebookLMでKMPのソースを検索してもらうと海外での導入事例がたくさん引っかかったので比較的新しめの技術などはこの方法でキャッチアップをしていくのが今のところ最適解なのかなと思っています。
