🤖
KMPでロジックの共通化やiOS側から呼ぶのをやってみた
KMP TUTORIAL
KMPの学習を再開して2日目。Android側の実装はつかめていたと思えば、配置する場所が決まっているようだ。同じKotlinでも配置する場所が違う。
カウンターを作るだけで詰まった💦
プロジェクト構造
KMPLessonApp/
├── composeApp/
│ ├── src/
│ │ ├── commonMain/ # 共通のKotlinコード
│ │ │ └── kotlin/
│ │ └── iosMain/ # iOS固有のKotlinコード
│ │ └── kotlin/
├── iosApp/ # iOSアプリケーション
│ └── iosApp/
└── build.gradle.kts # プロジェクト設定
セットアップ手順
1. 環境設定
必要なツール:
- Android Studio
- Xcode 16以降
- JDK 18以降
- Kotlin Multiplatform Mobile (KMM) プラグイン
2. プロジェクト作成
- Android Studioで新規KMPプロジェクトを作成
- テンプレートから「KMP Application」を選択
- プロジェクト名と保存場所を設定
3. 共通コードの作成
-
composeApp/src/commonMain/kotlin/
に共通のコードを配置 - 例:カウンター機能の実装
class Counter {
private var count: Int = 0
fun increment() {
count++
}
fun decrement() {
count--
}
fun getCount(): Int {
return count
}
}
4. iOSブリッジの作成
これは結構詰まった。
-
composeApp/src/iosMain/kotlin/
にブリッジコードを配置 -
@ObjCName
アノテーションを使用してSwiftから呼び出し可能に
@OptIn(ExperimentalObjCName::class)
@ObjCName(name = "CounterBridge")
class CounterBridge {
private val counter = Counter()
@ObjCName(name = "onCountChanged")
var onCountChanged: ((NSNumber) -> Unit)? = null
fun increment() {
counter.increment()
onCountChanged?.invoke(NSNumber(counter.getCount()))
}
fun decrement() {
counter.decrement()
onCountChanged?.invoke(NSNumber(counter.getCount()))
}
fun getCount(): NSNumber {
return NSNumber(counter.getCount())
}
}
5. SwiftUIでの利用
struct ContentView: View {
@State private var counter = CounterBridge()
@State private var count: Int = 0
var body: some View {
VStack(spacing: 20) {
Text("Count: \(count)")
.font(.title)
HStack(spacing: 30) {
Button(action: {
counter.decrement()
}) {
Image(systemName: "minus.circle.fill")
.font(.system(size: 44))
}
Button(action: {
counter.increment()
}) {
Image(systemName: "plus.circle.fill")
.font(.system(size: 44))
}
}
}
.padding()
.onAppear {
count = counter.getCount().intValue
counter.onCountChanged = { newValue in
count = newValue.intValue
}
}
}
}
重要なポイント
-
共通コード
-
commonMain
ディレクトリには、プラットフォーム共通のロジックを配置 - プラットフォーム固有の機能は使用不可
-
-
ブリッジコード
-
@ObjCName
アノテーションで、Swiftからアクセスできるようにクラスやメソッドを公開 - プリミティブ型の変換(Int → NSNumber など)が必要
- コールバックを使用して状態変更を通知
-
-
SwiftUI実装
-
@State
を使用して状態を管理 -
onAppear
でコールバックを設定 - UIKitではなくSwiftUIを使用してモダンなUI実装
-
ビルドと実行
-
Android Studio
- Gradleビルドを実行
-
composeApp
モジュールを選択してビルド
-
Xcode
-
iosApp
ディレクトリをXcodeで開く - ビルドターゲットを選択して実行
-
トラブルシューティング
-
ブリッジコードが認識されない
- Xcodeプロジェクトを再ビルド
-
pod install
を実行
-
状態更新が反映されない
- コールバックが正しく設定されているか確認
- メインスレッドでの更新を確認
Discussion