Kotlin multiplatform mobileにAndroidアプリを移行

Android Studioで新規でKMMプロジェクトを作成すると、build.gradle.ktsで作成されるのでbuild.gradleを使っていた場合は移行が必要。
を参考に移行する。
プロジェクトレベルのbuild.gradleに定義していたライブラリの変数は、以下のように移行する。
//移行前
buildscript {
ext {
kotlin_version = '1.8.20'
}
}
//変数宣言なしにどこからでも参照可能
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
}
//移行後
buildscript {
extra.apply {
set("kotlin_version", "1.8.20")
}
}
//使用する箇所で変数を宣言する
val kotlin_version: String by project
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
}
が、buildSrcやバージョンカタログを使ったほうが筋は良さそう。

順番が前後するが、Koinの公式セットアップサンプルがbuildSrcのやり方をしているので、それを踏襲しextを使うのはやめる。
buildSrc方式でやるには以下の手順で行う。- プロジェクト直下にbuildSrcディレクトリを作成。
- build.gradle.ktsをbuildSrcディレクトリ直下に作成し以下を記述。
plugins {
`kotlin-dsl`
}
repositories {
mavenCentral()
}
- buildSrc/src/main/java/Dependencies.ktを作成し以下を記述。
object Versions {
const val koin = "3.2.0"
}
object Deps {
object Koin {
const val core = "io.insert-koin:koin-core:${Versions.koin}"
const val test = "io.insert-koin:koin-test:${Versions.koin}"
const val android = "io.insert-koin:koin-android:${Versions.koin}"
}
}
上記はKoinの例だが、他のdependenciesでも同様にVersions配下にバージョンを書けば、グローバルに参照できるようになる。

HiltはJavaクラスを生成するDIライブラリなので、commonMainで使用することはできない。
KoinはピュアKotlinのライブラリなので、commonMainでDIしたい場合はこちらを使う。
Syer10 1 yr. ago
You can't use Dagger with KMM since Dagger generates java classes, which cannot be used with Kotlin Multiplatform. The closest thing to Dagger for Kotlin Multiplatform is Kotlin-Inject, but people find Koin easier to use since it's closer to a Service Locator then a full DI.
Koin is a pure Kotlin library and can be used in your shared Kotlin project.

Hiltが使えないことに気づくまで、kaptをbuild.gradle.ktsで使う方法を調べていたが無駄になってしまった。
普通に
dependencies {
kapt("com.google.dagger:hilt-android-compiler:$hilt_version")
}
と書くと、sync時にエラーが起きる。
dependencies {
implementation("com.google.dagger:hilt-compiler:2.37")
// configure kapt to utilize
configurations.getByName("kapt").dependencies.add(
org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency(
"com.google.dagger",
"hilt-compiler",
"2.37"
)
)
}
のようにしたらsync自体はできたが、結局commonMain配下のソースコード中でhiltのアノテーションをインポートできなかった。
stackoverflowの回答をよく読んだらandroidMainの中でhiltを使う方法だった。

buildSrcの中でライブラリバージョンやライブラリ名を定義していればあとはsharedのbuild.gradle.ktsのdependenciesで呼び出すだけでKoinが使えるようになる。
sourceSets {
val commonMain by getting {
dependencies {
with(Deps.Koin) {
api(core)
api(test)
}
}
}

cocoaPodsのありなしで、共通モジュールのbuild.gradle.ktsへのiOSの指定の仕方が異なる。
cocoaPodsあり
kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
version = "1.0"
ios.deploymentTarget = "14.1"
podfile = project.file("../iosApp/Podfile")
framework {
baseName = "shared"
}
}
cocoaPodsなし
kotlin {
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}

Koinのイニシャライズ
Android
androidApp配下のMainApplication.kt
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
//モジュール内でContextを読み込みたい場合、以下が必要。
//またandroidContext()を呼び出すためには、androidMainに※のdependencyが必要
androidContext(this@MainApplication)
//モジュールの読み込み
modules(appModule() + androidModule)
}
}
}
sharedのbuild.gradle.kts
val androidMain by getting {
dependsOn(commonMain)
dependencies {
with(Deps.Koin) {
api(android)
}
}
}
androidはbuildSrcのDependencies.ktで定義している。
object Deps {
object Ksp {
const val ksp = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:${Versions.kspPlugin}"
}
object Koin {
const val core = "io.insert-koin:koin-core:${Versions.koin}"
const val test = "io.insert-koin:koin-test:${Versions.koin}"
const val android = "io.insert-koin:koin-android:${Versions.koin}"
const val annotations = "io.insert-koin:koin-annotations:${Versions.koinAnnotations}"
const val kspCompiler = "io.insert-koin:koin-ksp-compiler:${Versions.kspPlugin}"
}
}
iOS
iosMain内に定義したHelper.ktのinitKoin()を呼び出す。
shared/iosMain/kotlin/Helper.kt
fun initKoin(){
startKoin {
modules(appModule())
}
}
iosApp/iosApp/iOSApp.swift
@main
struct iOSApp: App {
init() {
HelperKt.doInitKoin()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

sync時に発生するエラーが解消できない
サンプルプロジェクトなどを参考に見様見真似で設定ファイルをいじってきたが、以下のようなエラーがsync時に発生してにっちもさっちもいかなくなった。
Caused by: java.lang.IllegalStateException: Artifacts of dependency org.jetbrains.kotlinx:kotlinx-coroutines-core-metadata:1.4.1 is built by old Kotlin Gradle Plugin and can't be consumed in this way
build.gradle.ktsやその他のファイル群も確認したが、サンプルとどこが違うのか特定できず。
検索しても情報がでてこずお手上げ。
結局KMMプロジェクトをいちから作成して、既存のソースコードを移植する方針に変更。