KMMプロジェクトを作ってバージョンカタログが利用できるスタート地点に立つまで

## はじめに
設定が複雑で設定やちゃんと動くようにするだけで一苦労。
とりあえず現時点での環境でプロジェクトが動くようになるまでまとめておく。後々別環境とかで動かす機会があったら、最初から環境構築から書きたい。
ライブラリのバージョンカタログ、バージョン管理ができるようになるまでします。

## 環境
* Macbook Air M1 2020
* iOS 12.1 Monterey
* Android Studio Bumblebee | 2021.1.1 Patch 1
* Kotlin Multiplatform Mobile plugin 0.3.2
* Xcode 13.2.1

## プロジェクトが動くようになるまでの手順
1. Android StudioでNew Project->Kotlin Multiplatform App
2. 名前を任意で決めて、OSバージョンは２９〜
3. アプリ名は、androidApp,iosApp,shared
4. iOS frameworkは、　CocoaPods dependency frameworkを利用
5. Android Studioから、シミュレータで、Androidアプリ起動
6. iOSは、XcodeからiosApp/iosApp.workspaceから開き、シミュレータ起動（Android Studioからは、なぜか開けない）

## gradleの設定(必要な人だけ)
* gradle-wapper.properties(初期のまま)
```
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
```

* gradle.properties(初期のまま)
```
#Gradle
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"

#Kotlin
kotlin.code.style=official

#Android
android.useAndroidX=true

#MPP
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.native.enableDependencyPropagation=false
kotlin.mpp.enableCInteropCommonization=true
```

* settings.gradle.kts
バージョンカタログを導入します。
```kotlin
pluginManagement {
    repositories {
        google()
        gradlePluginPortal()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "appName"
include(":androidApp")
include(":shared")

enableFeaturePreview("VERSION_CATALOGS")
```

* libs.versions.toml
gradleディレクトリに作成します。
バージョンカタログに利用します。

```toml
[versions]
plugin-android = "7.1.1"
plugin-kotlin = "1.6.10"
plugin-gver = "0.42.0"

androidx-compose = "1.1.0"
kotlinx-serialization = "1.3.2"
kotlinx-coroutines = "1.6.0-native-mt"
ktor = "2.0.0-beta-1"
napier = "2.4.0"
multiplatform-settings = "0.8.1"
voyager = "1.0.0-beta15"
koin = "3.1.5"
accompanist = "0.23.0"
coil = "2.0.0-alpha08"
activity-compose = "1.4.0"
work-runtime-ktx = "2.7.1"
desugar-jdk-libs = "1.1.5"

[libraries]
[versions]
plugin-android = "7.1.1"
plugin-kotlin = "1.6.10"
plugin-gver = "0.42.0"

androidx-compose = "1.1.0"
kotlinx-serialization = "1.3.2"
kotlinx-coroutines = "1.6.0-native-mt"
ktor = "2.0.0-beta-1"
napier = "2.4.0"
multiplatform-settings = "0.8.1"
voyager = "1.0.0-beta15"
koin = "3.1.5"
accompanist = "0.23.0"
coil = "2.0.0-alpha08"
activity-compose = "1.4.0"
work-runtime-ktx = "2.7.1"
desugar-jdk-libs = "1.1.5"
appcompat = "1.3.1"
androidx-core = "1.7.0"
androidx-lifecycle = "2.4.1"

[libraries]
plugin-android = { module = "com.android.tools.build:gradle", version.ref = "plugin-android" }
plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "plugin-kotlin" }
plugin-kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "plugin-kotlin" }
plugin-gver = { module = "com.github.ben-manes:gradle-versions-plugin", version.ref = "plugin-gver" }

androidx-core = {module = "androidx.core:core-ktx", version.ref = "androidx-core"}
androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidx-compose" }
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "androidx-compose" }
androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "androidx-compose" }
androidx-compose-material = { module = "androidx.compose.material:material", version.ref = "androidx-compose" }
androidx-compose-material-icons-core = {module = "androidx.compose.material:material-icons-core", version.ref = "androidx-compose"}
androidx-compose-material-icons-extended = {module = "androidx.compose.material:material-icons-extended", version.ref = "androidx-compose"}
androidx-compose-runtime-livedata = {module = "androidx.compose.runtime:runtime-livedata", version.ref = "androidx-compose"}
androidx-compose-runtime-rxjava2 = {module = "androidx.compose.runtime:runtime-rxjava2", version.ref = "androidx.compose"}
androidx-compose-ui-test = {module = "androidx.compose.ui:ui-test-junit4", version.ref = "androidx.compose"}
androidx-lifecycle = {module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle"}

ktor-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
napier = { module = "io.github.aakira:napier", version.ref = "napier" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
ktor-client-ios = { module = "io.ktor:ktor-client-ios", version.ref = "ktor" }
ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" }
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" }
accompanist-insets = { module = "com.google.accompanist:accompanist-insets", version.ref = "accompanist" }
accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version.ref = "accompanist" }
work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "work-runtime-ktx" }
desugar-jdk-libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar-jdk-libs" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat"}

[bundles]
plugins = ["plugin-android", "plugin-kotlin",  "plugin-kotlin-serialization", "plugin-gver"]
```


* build.gradle.kts
いろいろとやり方があると思いますが。
ライブラリのバージョン管理も導入します。(plugins{}でben-manes.versionsを指定)
ややこしいですが、この辺がややこしいです。
まず一旦この時点でbuild.gradle(shared)のすべてのコードを削除しておきます。
import文、tasks.withType<DependencyUpdatesTask>、dependencies {classpath(libs.bundles.plugins)}を除してから、syncします。
次に、import文、tasks.withType<DependencyUpdatesTask>を加えて、syncします。
その後dependencies {classpath(libs.bundles.plugins)}を加えてsyncし、build.gradle(shared)を復活させます。



```kotlin
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask

buildscript {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
    dependencies {
        classpath(libs.bundles.plugins)
    }
    extra["versions"] = mapOf(
        "compose_version" to "1.0.1"
    )
}

plugins {
    id ("com.android.application") version "7.1.1" apply false
    id ("com.android.library") version "7.1.1" apply false
    kotlin("android") version "1.5.31" apply false
    id ("com.github.ben-manes.versions") version "0.42.0"
}

tasks.register("clean", Delete::class) {
    delete(rootProject.buildDir)
}

fun isNonStable(version: String): Boolean {
    val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.toUpperCase().contains(it) }
    val regex = "^[0-9,.v-]+(-r)?$".toRegex()
    val isStable = stableKeyword || regex.matches(version)
    return isStable.not()
}

tasks.withType<DependencyUpdatesTask> {
    rejectVersionIf {
        isNonStable(candidate.version) && !isNonStable(currentVersion)
    }
}
```

build.gradle(androidApp)
```kotlin
val versions : Map<String, String> by project

plugins {
    id("com.android.application")
    kotlin("android")
}

android {
    compileSdk = 32
    defaultConfig {
        applicationId = "com.example.playground.android"
        minSdk = 29
        targetSdk = 32
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.2.0-alpha04"
    }
    packagingOptions {
        exclude("META-INF/AL2.0")
        exclude("META-INF/LGPL2.1")
    }
}

dependencies {
    implementation(project(":shared"))
    implementation(libs.androidx.appcompat)
    implementation(libs.androidx.compose.ui)
    implementation(libs.androidx.compose.ui.tooling)
    implementation(libs.androidx.compose.foundation)
    implementation(libs.androidx.compose.material)
    implementation(libs.androidx.compose.material.icons.core)
    implementation(libs.androidx.compose.material.icons.extended)
    implementation(libs.androidx.compose.runtime.livedata)
    implementation(libs.androidx.compose.runtime.rxjava2)
    implementation(libs.androidx.compose.ui.test)
    implementation(libs.activity.compose)
    implementation(libs.androidx.core)
    implementation(libs.androidx.lifecycle)
}
```

* コードを書く
自動で生成されるMainActivityのコードはcomposeでないので、composeに直す。