はじめに:Kotlin Multiplatform (KMP) とは? なぜ今注目なのか?
クロスプラットフォーム開発の世界では、これまで「UIまで含めてすべてを共通化する」アプローチ(React Native, Flutterなど)が主流でした。しかし、プラットフォーム固有のユーザー体験を損なうという課題も指摘されてきました。
Kotlin Multiplatform (KMP) は、この問題に対する新しい答えを提示します。KMPは、UIを除くビジネスロジック、データ層、ネットワーク通信といったアプリケーションの頭脳部分をKotlinで一度だけ書き、それをiOSとAndroidの両方で共有する技術です。
KMPの最大の魅力は、以下の2つの世界の「良いとこ取り」ができる点です。
- 開発効率の向上: 重要なビジネスロジックを共通化することで、コードの重複をなくし、バグの発生を抑え、開発スピードを向上させます。
- ネイティブなUI/UXの実現: UI部分は各プラットフォームの最新技術(AndroidではJetpack Compose、iOSではSwiftUI)を使ってネイティブに実装します。これにより、ユーザーはプラットフォームに最適化された滑らかな操作感を享受できます。
2023年11月にStable版がリリースされ、GoogleやJetBrainsの強力なサポートのもと、KMPは2025年現在、本番環境で採用できる信頼性の高い選択肢となっています。
本記事では、KMPを使って簡単なニュースアプリを開発する手順を通して、その実践的な使い方を学びます。
最短で課題解決する一冊
この記事の内容と高い親和性が確認できたベストマッチです。早めにチェックしておきましょう。
KMPのアーキテクチャ:何を共通化し、何をネイティブに残すか
KMPプロジェクトは、一般的に3つの主要なモジュールで構成されます。
shared: 共通化するコードを置くモジュール。Kotlinで記述します。commonMain: プラットフォームに依存しない純粋なKotlinコード(ビジネスロジック、データクラスなど)。androidMain: Android固有のAPI(Context, SharedPreferencesなど)にアクセスする必要がある場合のコード。iosMain: iOS固有のAPI(CoreFoundation, UIKitなど)にアクセスする必要がある場合のコード。
androidApp: Androidアプリのモジュール。UIはJetpack Composeで実装し、sharedモジュールをライブラリとして利用します。iosApp: iOSアプリのモジュール。UIはSwiftUIで実装し、sharedモジュールをフレームワークとして利用します。
このアーキテクチャにより、ロジックの変更はsharedモジュール一箇所だけで済み、両プラットフォームに即座に反映されます。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
開発環境のセットアップ
KMP開発には、Android StudioとKotlin Multiplatform Mobileプラグインが必要です。
- 最新版のAndroid Studioをインストールします。
- 「Preferences/Settings」>「Plugins」から「Kotlin Multiplatform Mobile」を検索し、インストールします。
- iOSアプリをビルド・実行するために、Xcodeもインストールしておきます。
準備ができたら、Android Studioの「New Project」から「Kotlin Multiplatform App」を選択し、ウィザードに従ってプロジェクトを作成します。iOS framework distributionは「Regular framework」を選択するのがシンプルで始めやすいでしょう。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
ステップ1:共有モジュール (shared) でAPIクライアントを実装する
sharedモジュールに、プラットフォーム非依存のHTTPクライアントであるKtorと、JSONのシリアライズ/デシリアライズを行うKotlinx Serializationを導入します。
shared/build.gradle.ktsのdependenciesブロックに以下を追加します。
// ...
sourceSets {
val commonMain by getting {
dependencies {
// Ktor for networking
implementation("io.ktor:ktor-client-core:2.3.11")
implementation("io.ktor:ktor-client-content-negotiation:2.3.11")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.11")
// Kotlinx Serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-okhttp:2.3.11")
}
}
val iosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-darwin:2.3.11")
}
}
}
// ...次に、commonMainにAPIから取得する記事データを表すArticle.ktと、実際に通信を行うNewsApi.ktを作成します。
// shared/src/commonMain/kotlin/com/example/myapp/Article.kt
package com.example.myapp
import kotlinx.serialization.Serializable
@Serializable
data class Article(
val title: String,
val description: String?,
val url: String
)
// shared/src/commonMain/kotlin/com/example/myapp/NewsApi.kt
package com.example.myapp
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.serialization.kotlinx.json.json
class NewsApi {
private val client = HttpClient {
install(ContentNegotiation) {
json()
}
}
// suspend関数として定義することで、非同期処理を簡潔に書ける
suspend fun fetchArticles(): List<Article> {
// 実際にはここにニュースAPIのURLを入れる
val response = client.get("https://api.example.com/news")
return response.body()
}
}さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
ステップ2:Androidアプリから共有ロジックを呼び出す
androidAppモジュールで、Jetpack Composeを使ってUIを構築し、sharedモジュールのNewsApiを呼び出します。
// androidApp/src/main/java/com/example/myapp/android/MainActivity.kt
package com.example.myapp.android
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Text
import androidx.compose.runtime.*
import com.example.myapp.NewsApi
class MainActivity : ComponentActivity() {
private val newsApi = NewsApi()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var articles by remember { mutableStateOf(emptyList<com.example.myapp.Article>()) }
// Composableのライフサイクルに合わせてコルーチンを起動
LaunchedEffect(Unit) {
articles = newsApi.fetchArticles()
}
LazyColumn {
items(articles) { article ->
Text(text = article.title)
}
}
}
}
}さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
ステップ3:iOSアプリから共有ロジックを呼び出す
iOSでは、sharedモジュールがコンパイルされてshared.frameworkという形で提供されます。SwiftUIからこれを呼び出します。
Swiftはsuspend関数を直接呼び出せませんが、KMPはsuspend関数をcompletionHandler付きのコールバック形式に自動変換してくれます。
// iosApp/iosApp/ContentView.swift
import SwiftUI
import shared // sharedフレームワークをインポート
struct ContentView: View {
@State private var articles: [Article] = []
private let newsApi = NewsApi()
var body: some View {
List(articles, id: \.url) { article in
Text(article.title)
}
.onAppear {
// APIを呼び出す
newsApi.fetchArticles { fetchedArticles, error in
if let fetchedArticles = fetchedArticles {
self.articles = fetchedArticles
}
}
}
}
}これで、AndroidとiOSの両方で、同じKotlinで書かれたロジックを共有するアプリが完成しました。ロジックの修正はsharedモジュールだけで済みます。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
まとめ:ネイティブの体験と開発効率の両立
Kotlin Multiplatformは、モバイルアプリ開発における「銀の弾丸」ではないかもしれませんが、「開発効率」と「ネイティブなユーザー体験」という、これまでトレードオフの関係にあった2つの要素を高いレベルで両立させる強力な選択肢です。
- ロジックは一度書けばOK: Kotlinで書いたビジネスロジックは、プラットフォームを意識することなく再利用できます。
- UIはネイティブで: SwiftUIとJetpack Composeという、各プラットフォームで最もモダンで表現力豊かなUIフレームワークを最大限に活用できます。
- 既存アプリへの導入も可能: 新規プロジェクトだけでなく、既存のネイティブアプリの一部をKMPで置き換えていく、漸進的な導入も可能です。
もしあなたがネイティブアプリのパフォーマンスやUXに妥協したくない、でも開発効率も上げたい、と考えているなら、Kotlin Multiplatformは試してみる価値のある技術です。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。





