Kotlin Multiplatform Mobileの活用法

要約

KMMで始める共通ロジック開発: iOS/Androidアプリを効率化 2026年版

Kotlin Multiplatform Mobile (KMM) を活用し、iOSとAndroidアプリ間で共通のビジネスロジックを効率的に共有する方法を解説します。

Keywords: KMM, 共通ロジック, モバイル開発効率化

目次

1. はじめに: 2026年のモバイル開発とKMMの台頭

2. Kotlin Multiplatform Mobile (KMM) の基本とアーキテクチャ

3. KMM開発環境のセットアップとプロジェクト構造

4. 共通ロジックの実装: データ層とビジネスロジック

5. KMM導入における課題と解決策

6. KMMのベストプラクティスとパフォーマンス最適化

7. 2026年におけるKMMの展望と将来性

8. よくある質問 (FAQ)

イントロダクション

はじめに: 2026年のモバイル開発とKMMの台頭


今日のモバイルアプリケーション開発は、日々進化する技術とユーザーの期待に応えるため、常に効率性と品質のバランスを模索しています。特に、iOSとAndroidという二大プラットフォーム向けにアプリケーションを開発する場合、多くの企業や開発者が共通の課題に直面しています。それは、ビジネスロジック、データ層、ネットワーク通信など、プラットフォームに依存しない部分のコードが重複しがちであるという点です。

このコードの重複は、開発コストの増大、メンテナンスの複雑化、そして機能追加やバグ修正の際に両プラットフォームで同じ変更を適用する必要があるという非効率性を生み出します。例えば、新しいAPIエンドポイントが追加された場合、iOSアプリのSwiftコードとAndroidアプリのKotlinコードの両方で、それぞれのモデルクラスやネットワークリクエストロジックを更新しなければなりません。これは、開発サイクルを長くし、市場投入までの時間を延ばす要因となります。

このような背景から、クロスプラットフォーム開発フレームワークが注目を集めてきました。React NativeやFlutterといったフレームワークは、UI層も含めてコードベースを共有することで、開発効率の向上に貢献してきました。しかし、これらのフレームワークは、ネイティブのUIコンポーネントやエコシステムとの統合において、時に制約やパフォーマンスの課題を抱えることがあります。ネイティブのルック&フィールやパフォーマンスを犠牲にすることなく、開発効率を高める方法はないのでしょうか?

ここで登場するのが、JetBrainsが開発したKotlin Multiplatform Mobile (KMM) です。KMMは、Kotlin言語のマルチプラットフォーム機能を活用し、iOSとAndroidの間でビジネスロジック、データモデル、ネットワーク層などの非UIコードを共有することを可能にします。これにより、開発者は各プラットフォームでネイティブのUIフレームワーク(iOSのUIKit/SwiftUI、AndroidのJetpack Compose/Viewシステム)を使用しつつ、中核となるロジックは一度書くだけで済むようになります。

2026年現在、KMMは成熟度を増し、多くの企業がその導入を検討、あるいは既に成功させています。特に、既存のネイティブアプリに段階的に導入しやすいという特性は、大規模なリプレイスを避けたい企業にとって大きな魅力となっています。このブログ記事では、KMMの基本的な概念から、実際の開発環境のセットアップ、共通ロジックの実装方法、そして導入における課題と解決策、さらには2026年におけるKMMの展望まで、幅広く深く掘り下げていきます。KMMを活用して、あなたのモバイル開発をより効率的で持続可能なものに変えるための一歩を踏み出しましょう。

KMMの基礎

Kotlin Multiplatform Mobile (KMM) の基本とアーキテクチャ


KMMを理解する上で最も重要なのは、その「マルチプラットフォーム」という概念です。KMMは、単一のコードベースから複数のプラットフォーム(iOS、Android、Web、デスクトップなど)向けのコードを生成できるKotlinの機能「Kotlin Multiplatform」を、特にモバイル(iOSとAndroid)に特化させたものです。しかし、KMMの目的はUIまで共有することではなく、あくまでプラットフォームに依存しないロジック部分を共有することにあります。

KMMのアーキテクチャ概要

KMMプロジェクトは、主に以下の3つのモジュールで構成されます。

1. Shared モジュール (Common Main): ここに、iOSとAndroidの両方で利用する共通のビジネスロジック、データモデル、ネットワーク層などがKotlinで記述されます。このモジュールは、プラットフォーム固有のAPIに直接アクセスすることはできません。

2. Android モジュール (Android Main): 通常のAndroidアプリケーション開発と同様に、UI(Activity, Fragment, Jetpack Composeなど)やAndroid固有の機能(Android SDK API、特定デバイス機能など)を実装します。Sharedモジュールで定義された共通ロジックを呼び出して利用します。

3. iOS モジュール (iOS Main): iOSアプリケーション開発と同様に、UI(ViewController, SwiftUIなど)やiOS固有の機能(iOS SDK API、Core Dataなど)を実装します。SharedモジュールはKotlin/NativeによってiOSが利用できるフレームワークに変換され、Swift/Objective-Cから呼び出すことができます。

KMM Architecture Diagram

Expect/Actualメカニズムの解説

KMMのSharedモジュールはプラットフォーム固有のAPIに直接アクセスできないと述べましたが、では、例えばデバイスのUUIDを取得したり、特定のストレージ機能を利用したりするなど、プラットフォーム固有の機能が必要な場合はどうすればよいでしょうか?ここで登場するのが「Expect/Actual」メカニズムです。

Expect/Actualは、Sharedモジュールでプラットフォーム固有の機能の「期待値 (Expectation)」を宣言し、各プラットフォームモジュール(Android、iOS)でその「実装 (Actual)」を提供する仕組みです。これにより、Sharedモジュールはプラットフォーム固有の機能に依存することなく、抽象化された形でそれらを呼び出すことができます。

コード解説

以下は、共通モジュールで期待される関数を宣言し、AndroidとiOSでそれぞれ具体的な実装を提供する例です。これにより、共通ロジックはプラットフォーム固有のUUID取得方法を知る必要がなくなります。

// commonMain/kotlin/com/kwonteki/platform/Platform.kt
package com.kwonteki.platform

expect class Platform() {
    val name: String
    fun getDeviceId(): String
}

// androidMain/kotlin/com/kwonteki/platform/Platform.android.kt
package com.kwonteki.platform

import android.os.Build
import java.util.UUID

actual class Platform actual constructor() {
    actual val name: String = "Android ${Build.VERSION.SDK_INT}"
    actual fun getDeviceId(): String {
        // Android固有のデバイスID取得ロジック (例: Advertising ID, Instance IDなど)
        // ここでは簡略化のためUUIDを生成
        return UUID.randomUUID().toString()
    }
}

// iosMain/kotlin/com/kwonteki/platform/Platform.ios.kt
package com.kwonteki.platform

import platform.UIKit.UIDevice
import platform.Foundation.NSUUID

actual class Platform actual constructor() {
    actual val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion()
    actual fun getDeviceId(): String {
        // iOS固有のデバイスID取得ロジック (例: identifierForVendor)
        return NSUUID.UUID().UUIDString()
    }
}

KMMのメリットとデメリット

KMMを導入する前に、そのメリットとデメリットを明確に理解しておくことが重要です。

メリット

コードの再利用性向上: ビジネスロジック、データ層、ネットワーク層など、非UIコードをiOSとAndroid間で90%以上共有できます。これにより、開発コストと時間の削減、バグの減少に貢献します。

ネイティブUIの維持: UIは各プラットフォームのネイティブ技術で開発されるため、ユーザーは最高のパフォーマンスと使い慣れたルック&フィールを享受できます。これにより、ユーザーエクスペリエンスの質が保たれます。

段階的な導入が可能: 既存のネイティブプロジェクトにKMMモジュールを少しずつ組み込むことができます。全面的なリプレイスが不要なため、リスクを抑えながら導入を進められます。

Kotlin言語の活用: Android開発者にとって馴染み深いKotlin言語を使用するため、学習コストが低く、既存のKotlinエコシステム(コルーチン、シリアライゼーションなど)を最大限に活用できます。

Swift/Objective-Cとの高い相互運用性: Kotlin/Nativeが生成するフレームワークは、SwiftやObjective-Cからシームレスに呼び出すことができ、ネイティブコードとの連携がスムーズです。

デメリット

学習コスト: Android開発者にとってはKMMの概念や設定は比較的スムーズですが、iOS開発者にとってはKotlin/NativeやGradleの知識が新たに必要になります。

複雑なビルド設定: Gradleのマルチプラットフォーム設定は、特に初めての場合、複雑に感じることがあります。依存関係の管理やプラットフォーム固有のビルド設定には慣れが必要です。

ライブラリのサポート: 全てのサードパーティライブラリがマルチプラットフォームに対応しているわけではありません。プラットフォーム固有のライブラリを使用する必要がある場合、Expect/Actualでラップするか、代替を探す必要があります。

デバッグの複雑さ: 共通モジュールで発生した問題のデバッグは、プラットフォーム固有の部分に比べて多少複雑になることがあります。特にiOS側でのKotlin/NativeのデバッグにはXcodeとIDEの連携が必要になります。

ポイント

KMMは、コード再利用性とネイティブUIの維持という二つの大きなメリットを両立しますが、ビルド設定の複雑さやiOS開発者の学習コストなど、導入前の考慮事項も存在します。これらのバランスを理解することが成功の鍵です。

開発環境

KMM開発環境のセットアップとプロジェクト構造


KMMプロジェクトを始めるためには、いくつかのツールと環境設定が必要です。ここでは、基本的なセットアップ手順とプロジェクトの典型的な構造について解説します。

必要なツールと環境

KMM開発を行うには、以下のツールが必須です。

Android Studio: KMMプラグインをインストールして使用します。AndroidアプリとKMM共通モジュールの開発に利用します。バージョンは最新版を推奨します。

Xcode: iOSアプリの開発とKMM共通モジュールのiOS側ビルドに必要です。macOS環境でのみ利用可能です。

Kotlinプラグイン: Android Studioに標準で含まれていますが、最新の状態に保つことを確認してください。

KMMプラグイン (Kotlin Multiplatform Mobile Plugin): Android Studioのプラグインマーケットプレイスからインストールします。これにより、KMMプロジェクトの作成や設定が容易になります。

Java Development Kit (JDK): Android StudioとGradleの実行に必要です。

ポイント

KMM開発では、Android StudioとXcodeの両方が必須です。特にiOS開発を行う場合はmacOS環境が必要となるため、開発環境の選定段階でこの点を考慮しておく必要があります。

新規KMMプロジェクトの作成手順

KMMプラグインがインストールされていれば、Android Studioから簡単に新しいKMMプロジェクトを作成できます。以下にそのステップを示します。

1

Android Studioを起動

「New Project」を選択します。

2

KMMプロジェクトテンプレートの選択

「Kotlin Multiplatform App」テンプレートを選択し、「Next」をクリックします。

3

プロジェクトの設定

アプリケーション名、パッケージ名、保存場所などを入力します。iOS framework distributionオプション(CocoaPodsまたはregular framework)もここで選択できます。通常はデフォルトのRegular frameworkで問題ありません。

4

プロジェクトの作成

「Finish」をクリックすると、Android Studioがプロジェクトをセットアップし、必要なGradle同期を実行します。

Android Studio KMM Project Creation

KMMプロジェクトの典型的な構造

新しいKMMプロジェクトを作成すると、以下のようなモジュール構造が生成されます。

.idea/: IDE設定ファイル

gradle/: Gradleラッパー関連ファイル

androidApp/: Androidアプリケーションのソースコード、リソース、Gradleビルドファイルが含まれます。

iosApp/: iOSアプリケーションのXcodeプロジェクトファイル、Swift/Objective-Cコードが含まれます。

shared/: KMMのコアとなる共通モジュールです。以下のソースセットが含まれます。

commonMain/: 共通ロジック、データモデル、Expect宣言など、全てのプラットフォームで共有されるコード。

androidMain/: Android固有のActual実装、Android SDKに依存するコード。

iosMain/: iOS固有のActual実装、iOS SDKに依存するコード。

androidTest/: Androidプラットフォーム固有のテストコード。

iosTest/: iOSプラットフォーム固有のテストコード。

commonTest/: 共通ロジックのテストコード。

build.gradle.kts (project): プロジェクト全体のGradle設定ファイル。

settings.gradle.kts: プロジェクトに含まれるモジュールを定義するファイル。

Gradle設定の解説 (sharedモジュール)

KMMプロジェクトの心臓部の一つは、sharedモジュールのbuild.gradle.ktsファイルです。ここで、マルチプラットフォームターゲットの定義、依存関係の管理、iOSフレームワークのビルド設定などが行われます。

コード解説

以下は、KMMのshared/build.gradle.ktsの簡略化された例です。AndroidとiOSのターゲットがどのように定義され、共通の依存関係がどのように追加されるかを示しています。

// shared/build.gradle.kts
plugins {
    kotlin("multiplatform")
    kotlin("native.cocoapods") // iOS frameworkをCocoaPods経由で配布する場合
    id("com.android.library")
}

kotlin {
    androidTarget {
        compilations.all {
            kotlinOptions.jvmTarget = "1.8"
        }
    }
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    // iOS frameworkのビルド設定
    val iosTargets = listOf(iosX64(), iosArm64(), iosSimulatorArm64())
    iosTargets.forEach {
        it.binaries.framework {
            baseName = "Shared" // 生成されるフレームワーク名
            isStatic = true // 静的ライブラリとしてビルド
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                // 共通ライブラリの依存関係
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
                implementation("io.ktor:ktor-client-core:2.3.8")
                implementation("io.ktor:ktor-client-content-negotiation:2.3.8")
                implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.8")
            }
        }
        val androidMain by getting {
            dependencies {
                // Android固有の依存関係
                implementation("io.ktor:ktor-client-android:2.3.8")
            }
        }
        val iosMain by getting {
            dependencies {
                // iOS固有の依存関係
                implementation("io.ktor:ktor-client-darwin:2.3.8")
            }
        }
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
            }
        }
    }
}

android {
    namespace = "com.kwonteki.shared"
    compileSdk = 34
    defaultConfig {
        minSdk = 24
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
}

// CocoaPods設定 (CocoaPodsを使用する場合)
// cocoapods {
//    summary = "Some description for the Shared Kotlin library"
//    homepage = "Link to the Shared Kotlin library's homepage"
//    version = "1.0"
//    ios.deploymentTarget = "14.1"
//    framework {
//        baseName = "Shared"
//        isStatic = true
//    }
// }

このGradleファイルでは、androidTargetiosX64(), iosArm64(), iosSimulatorArm64()でそれぞれAndroidとiOSのターゲットプラットフォームを定義しています。Kotlin/Nativeは、これらのiOSターゲット向けに共通モジュールをコンパイルし、最終的にiOSアプリで利用可能なフレームワークを生成します。また、sourceSetsブロック内で、commonMain, androidMain, iosMainそれぞれの依存関係を管理しています。これにより、共通のライブラリはcommonMainに、プラットフォーム固有のライブラリはそれぞれの*Mainに記述できるわけです。

KMM Project File Structure

実践的な実装

共通ロジックの実装: データ層とビジネスロジック


KMMの真価は、データ層とビジネスロジックを共通化することで発揮されます。ここでは、簡単なAPIクライアントとデータモデルをKMMのSharedモジュールで実装し、それをiOSとAndroidのUI層から利用する具体的な方法を見ていきましょう。

データモデルとAPIクライアントの共通化

例として、シンプルなユーザー情報を取得するAPIを考えます。このAPIはユーザーのIDと名前をJSON形式で返すとします。KMMでは、kotlinx.serializationライブラリとKtor clientを組み合わせて、データモデルの定義とネットワーク通信を共通化するのが一般的です。

コード解説

まずは、共通モジュール (commonMain) にデータクラスとAPIクライアントを定義します。これにより、iOSとAndroidは同じデータ構造とネットワークロジックを使用できます。

// shared/src/commonMain/kotlin/com/kwonteki/shared/data/model/User.kt
package com.kwonteki.shared.data.model

import kotlinx.serialization.Serializable

@Serializable
data class User(
    val id: String,
    val name: String,
    val email: String
)

// shared/src/commonMain/kotlin/com/kwonteki/shared/data/remote/UserService.kt
package com.kwonteki.shared.data.remote

import com.kwonteki.shared.data.model.User
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json

class UserService {
    private val httpClient = HttpClient {
        install(ContentNegotiation) {
            json(Json {
                ignoreUnknownKeys = true
                prettyPrint = true
                isLenient = true
            })
        }
    }

    suspend fun getUsers(): List<User> {
        // 実際にはAPIのエンドポイントを使用
        return httpClient.get("https://jsonplaceholder.typicode.com/users").body()
    }

    suspend fun getUserById(userId: String): User {
        return httpClient.get("https://jsonplaceholder.typicode.com/users/$userId").body()
    }
}

上記のコードでは、@Serializableアノテーションを使ってUserデータクラスを定義し、UserServiceクラスでKtor HttpClientを使用してAPI呼び出しを行っています。これらのクラスは全てcommonMain内にあり、プラットフォーム固有のコードは一切含まれていません。これにより、両プラットフォームで同じロジックとデータ構造が保証されます。

KMM Data Flow Diagram

プラットフォーム固有のUIからの利用

次に、この共通のUserServiceをAndroidとiOSのUIからどのように利用するかを見ていきましょう。

Androidからの利用

Androidでは、通常のKotlinコードと同様に、UserServiceをインポートして使用できます。コルーチンを使って非同期処理を行うのが一般的です。

コード解説

AndroidのViewModelからUserServiceを呼び出し、ユーザーリストを取得する例です。

// androidApp/src/main/java/com/kwonteki/androidapp/MainViewModel.kt
package com.kwonteki.androidapp

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.kwonteki.shared.data.model.User
import com.kwonteki.shared.data.remote.UserService
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class MainViewModel(
    private val userService: UserService = UserService()
) : ViewModel() {

    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users

    init {
        fetchUsers()
    }

    private fun fetchUsers() {
        viewModelScope.launch {
            try {
                _users.value = userService.getUsers()
            } catch (e: Exception) {
                // エラーハンドリング
                e.printStackTrace()
            }
        }
    }
}

iOSからの利用

iOSでは、KMMのSharedモジュールがGradleによってObjective-C/Swiftから利用可能なフレームワークにコンパイルされます。このフレームワークをXcodeプロジェクトに組み込むことで、SwiftコードからKotlinのクラスや関数を呼び出すことができます。KMMのコルーチンは、iOS側からはCompletion HandlerやCombine (SwiftUI) と連携するようにラップされることが多く、非同期処理もスムーズに扱えます。

コード解説

SwiftUIのViewModelからUserServiceを呼び出し、ユーザーリストを取得する例です。KMMのコルーチンをSwiftから扱うためのラッパー(例: FlowWrapperCallbackFlow)を使用することが一般的です。

// iosApp/iosApp/ContentView.swift (ViewModel部分のイメージ)
import SwiftUI
import Shared // Sharedフレームワークをインポート

class UserListViewModel: ObservableObject {
    @Published var users: [User] = []
    private let userService = UserService()

    init() {
        fetchUsers()
    }

    func fetchUsers() {
        // Sharedモジュールのコルーチン関数をSwiftから呼び出すためのラッパーを使用
        // 例: shared-module-coroutines-helperライブラリなど
        Task {
            do {
                let fetchedUsers = try await userService.getUsers()
                DispatchQueue.main.async {
                    self.users = fetchedUsers
                }
            } catch {
                print("Error fetching users: \(error)")
                // エラーハンドリング
            }
        }
    }
}

struct ContentView: View {
    @StateObject private var viewModel = UserListViewModel()

    var body: some View {
        NavigationView {
            List(viewModel.users, id: \.id) { user in
                VStack(alignment: .leading) {
                    Text(user.name)
                        .font(.headline)
                    Text(user.email)
                        .font(.subheadline)
                        .foregroundColor(.gray)
                }
            }
            .navigationTitle("Users")
        }
    }
}

ポイント

データモデルとAPIクライアントの共通化はKMMの最も強力なユースケースの一つです。Ktorとkotlinx.serializationを組み合わせることで、効率的かつ堅牢なネットワーク層を両プラットフォームで共有できます。iOS側でのコルーチン利用には、適切なラッパーの導入が鍵となります。

課題と解決策

KMM導入における課題と解決策


KMMは多くのメリットを提供する一方で、導入にはいくつかの技術的な課題が伴います。これらの課題を事前に理解し、適切な解決策を講じることで、スムーズなKMMプロジェクトの推進が可能になります。

問題 01

複雑なGradle設定とビルド時間

KMMのGradle設定は、特にマルチプラットフォームのターゲット定義やiOSフレームワークのビルド設定において、初めて触れる開発者にとっては複雑に感じられることがあります。また、iOSフレームワークのビルドは時間がかかる場合があり、開発体験に影響を与える可能性があります。

解決策 — Gradle設定のベストプラクティスとビルド最適化

► Gradle Kotlin DSLの活用: Kotlin DSLを使用することで、より型安全で読みやすいGradle設定を記述できます。IDEの補完機能も利用できるため、設定ミスを減らせます。

► ビルドキャッシュの有効化: Gradleのビルドキャッシュを有効にすることで、以前のビルド結果を再利用し、ビルド時間を短縮できます。プロジェクトのgradle.propertiesorg.gradle.caching=trueを追加します。

► 必要なiOSターゲットのみビルド: 開発中は、iosSimulatorArm64()など、現在開発しているシミュレーター向けのターゲットのみをビルドするように設定を調整することで、ビルド時間を大幅に短縮できます。CI/CDでは全てのターゲットをビルドするようにします。

► ビルドシステムとの統合: XcodeプロジェクトのBuild PhaseにGradleタスクを直接追加する代わりに、CocoaPodsやSwift Package Manager (SPM) などのパッケージマネージャーを通じてKMMフレームワークを配布することで、Xcode側のビルド設定を簡素化できます。

// shared/build.gradle.kts (ビルドターゲットの調整例)
kotlin {
    androidTarget { /* ... */ }

    // 開発中はシミュレーターのみをターゲットにする
    // val iosTargets = listOf(iosSimulatorArm64())
    // リリースビルドやCIでは全てをターゲットにする
    val iosTargets = listOf(iosX64(), iosArm64(), iosSimulatorArm64())

    iosTargets.forEach {
        it.binaries.framework {
            baseName = "Shared"
            isStatic = true
            // デバッグビルドのみシンボルを含めるなど、設定を細かく調整可能
        }
    }
    // ...
}

問題 02

プラットフォーム固有APIへのアクセス

共通モジュールはプラットフォーム固有のAPIに直接アクセスできないため、デバイスのGPS情報、カメラ、ローカルストレージなど、特定のOS機能を利用する必要がある場合に課題となります。

解決策 — Expect/Actualメカニズムの活用

► Expect/Actualの徹底: プラットフォーム固有の機能が必要な場合は、必ずexpect宣言を共通モジュールに置き、各プラットフォームでactual実装を提供します。これにより、共通ロジックの純粋性を保ちつつ、プラットフォーム機能を利用できます。

► マルチプラットフォーム対応ライブラリの利用: 多くの一般的な機能(コルーチン、シリアライゼーション、HTTPクライアント、データベースなど)には、既にマルチプラットフォームに対応したKotlinライブラリが存在します。これらを積極的に利用することで、Expect/Actualの実装量を減らすことができます。

► インターフェースの活用: 複雑なプラットフォーム固有の機能の場合、共通モジュールでインターフェースを定義し、各プラットフォームでそのインターフェースを実装したクラスを提供する方法も有効です。依存性注入(DI)フレームワークと組み合わせることで、よりクリーンなアーキテクチャを構築できます。

// commonMain/kotlin/com/kwonteki/platform/Logger.kt
package com.kwonteki.platform

expect class Logger {
    fun log(message: String)
}

// androidMain/kotlin/com/kwonteki/platform/Logger.android.kt
package com.kwonteki.platform

import android.util.Log

actual class Logger actual constructor() {
    actual fun log(message: String) {
        Log.d("KMM_APP", message)
    }
}

// iosMain/kotlin/com/kwonteki/platform/Logger.ios.kt
package com.kwonteki.platform

import platform.Foundation.NSLog

actual class Logger actual constructor() {
    actual fun log(message: String) {
        NSLog("KMM_APP: %@", message)
    }
}

問題 03

iOS開発者の学習コスト

Android開発者にとってKotlinは馴染み深いですが、iOS開発者にとってはKotlin言語、コルーチン、Gradleなどの新しい技術要素を習得する必要があり、学習コストが高くなる可能性があります。

解決策 — ドキュメントとトレーニング、相互理解の促進

► 充実したドキュメントとコード例: プロジェクト内で