Kotlin MultiplatformではiOSやAndroidで共通の処理となるロジックやモデルなどをKotlinで書き、UIやプラットフォーム固有の部分についてはネイティブに任せます。Compose Multiplatformを利用すればプラットフォーム間で共通のUIを作成することも可能です。
目次
KMPとCMPの関係性について
- Kotlin Multiplatformはビジネスロジックや共通処理を異なるプラットフォームで共有
- Compose MultiplatformはUIをマルチプラットフォームで共有
疑問点
CMP実装の試してみようかと思っていたが、書き方自体がほぼJetpack Composeと同じ書き方っぽい。KMPでマルチプラットフォーム対応しているなら、Jetpack Compose × KMPでダメなのか?
↓
Jetpack ComposeはAndroid依存でandroidx.compose.*ライブラリを利用しているため、iOSでは利用することができない(iOS用のKotlin/Nativeコンパイラがandroidx.compose.*を解釈できない)
↓
JetBrainsが提供しているCMP専用のorg.jetbrains.composeをGradleに追加して下記の依存関係を定義することでiOS向けなどでCMPライブラリが利用できるようになる
build.gradle.kts
sourceSets {
commmonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
}
}
CMPにはマルチプラットフォームでビルドしたり、frameworkとして配布する仕組みはないためKMP基盤で成り立っている
KMPプロジェクトの作成方法について
プラグインなしで自力でGradleの周りの設定(ソースセット・ターゲット・依存関係)などを定義しようと試しましたが、エラーが出て難しかったので諦めました。
プラグインを利用する方法
Android Studioで「Kotlin Multiplatform」のプラグインをインストールする

New Project → Generic → Kotlin Multiplatformを選択

プロジェクトの設定後、プラットフォームを選択する画面が出るのでiOSのShared UIが選択されている状態で進めます

そうするとCMPを利用する際に必要な設定が全て完了された状態でプロジェクトが作成されます

Gradle設定周りで確認するファイルはCMPモジュールのbuild.gradle.kts
iOS用のFrameworkを生成する
下記で利用可能なGradleタスクを確認することができます。
./gradlew taskscocoapodsを設定ファイルで指定することでpodPublishDebugXCFrameworkタスクを実行してframeworkを生成することもできるみたいですが理解できていません…
プラグインを利用したサンプルプロジェクトの場合
cocoapodsを利用していないのでassembleDebugXCFrameworkなどになると思っていたのですが見つからず、linkDebugFrameworkIosArm64やlinkDebugFrameworkIosSimulatorArm64などを実行するとフレームワークが生成されました。
KMPSample/composeApp/build/bin/debugFramework/ComposeApp.frameworkサンプルプロジェクトではXcodeのBuild Phaseに下記が追加されていました。
cd "$SRCROOT/.."
./gradlew :composeApp:embedAndSignAppleFrameworkForXcode※プロジェクトにComposeApp.frameworkの追加やBuild Settings → Framework Search Pathsの設定が不要(誤情報でしたら申し訳ないです)
iOSでCMPコードを呼び出す
プラグインを利用した場合はiosAppという名前でiOSプロジェクトも一緒に作成されており、SwiftUIでCMPのコードを呼び出す実装まで一緒に作成されていました。
UIViewControllerRepresentableを利用してSwiftUIで利用する

UIKitで利用する

commonMainとiosMainで定義について
サンプルプロジェクトではSwift側で利用できるようにするためにiosMainの中で ComposeUIViewControllerを生成

プラットフォーム共通のものはcommonMainで定義して利用することができるが、AndroidやiOS固有のAPIを呼び出したい時はexpect/actualで定義する必要がある
[commonMain]
@Composable
expect fun hoge() [iosMain]
import platform.UIKit.UIApplication
@Composable
actual fun hoge() {
UIApplication.sharedApplication.canOpenUrl() {
}
// Composableを返す
}プロジェクトのビルドについて
ビルドする時はiosAppを選択してシミュレータで確認が可能

ビルドするとUIがマテリアル…

UIKit側のUIButtonなどに置き換えられているのではなく、Composableがそのまま描画されているみたいですね
おわりに
CMPではJetpack Composeの書き方でUI構築するのでAndroidが用意しているプラクティスなどを元に練習していこうと思います。