KMM (Kotlin Multiplatform Mobile) チュートリアルを読んで触ってみる
はじめに: KMM (Kotlin Multiplatform Mobile) とは
KMM: Kotlin Multiplatform Mobile はJetbrains社が開発を行っているプログラミング言語 Kotlin を使って、AndroidやiOS向け、つまりマルチプラットフォーム向けにアプリを開発するために作られた SDK (Software Development Kit) です。
Kotlinは、現在ではJavaに変わるAndroidアプリ開発として広く利用されています。
筆者である私も、本業ではKotlinを使ったAndroid開発を行っており、Kotlinという言語の使い勝手の良さをよく知っています。
そのようなKotlinが最近では、先述したようにKMMと呼ばれるモバイルマルチプラットフォーム向けのSDKが人気になってきているようで、私自身も「この流れに乗らなければ!」という気持ちでこの記事を書くことに至りました。
なので、今回は公式の Get started with Kotlin Multiplatform Mobile (執筆時点で2023年4月8日が最終更新のものを参照) というドキュメントを読みながら一緒にKMMの概要を掴んでいきましょう。
開発環境のセットアップ
AndroidとiOSアプリの作成に入る前に、まずはKMMの環境構築を行います。
もちろんiOSアプリを動かすには、MacOS環境が必要です。
参考までに私の環境は、M1 Macbook Air (8GB RAM) です。
加えて下記の環境が必要となります。
ツール | 補足 |
Android Studio | 開発で利用するIDE (統合開発環境) です。
デバイスエミュレータもこのAndroid Studio上で管理、実行されます。 |
Xcode | iOSアプリを作成する際に使うIDEですが、KMMでは主にバックグラウンドで動作します。
iOSアプリに対してSwiftやObject-Cのコードを利用せざるを得な苦なった場合に使います。 |
JDK (Java Development Kit) | いわゆるJavaの開発環境です。
KotlinはJVM (Java Virtual Machine) 上で動作するJava互換言語なので、必要になります。 |
Kotlin Multiplatform Mobile plugin | Android Studioのプラグインです。
設定方法は後述します。 |
Kotlin plugin | 基本Android Studioに元から組み込まれています。
が、もしかしたらバージョンが古い可能性があるので、最新版の利用が推奨されます。 |
Kotlin Multiplatform Mobile plugin
Settings / Preferences > Pluginsよりインストール & 再起動してください。
プロジェクト選択画面からもPluginsに飛べます。
KDocterでの環境チェック
MacOS環境ではKDockterと呼ばれるツールを使ってKMMの環境に不足がないかをチェックできます。
Homebrewでインストールして利用してみましょう。
$ brew install kdoctor
$ kdocter
Environment diagnose (to see all details, use -v option):
[✓] Operation System
[✓] Java
[✓] Android Studio
[✖] Xcode
✖ Xcode requires to perform the First Launch tasks
Launch Xcode and complete setup
[✖] Cocoapods
✖ CocoaPods requires your terminal to be using UTF-8 encoding.
Consider adding the following to ~/.zprofile
export LC_ALL=en_US.UTF-8
Conclusion:
✖ KDoctor has diagnosed one or more problems while checking your environment.
Please check the output for problem description and possible solutions.
このような感じで、不足している項目を教えてくれます。
上記Cocoapodsの指摘は具体的で良いのですが、Xcodeの「Launch Xcode and complete setup」は、ちょっと抽象的で戸惑いました。
調べてみると、このスレッド に辿り着き、私の場合これを試してみたところ解決しました。
$ kdoctor
Environment diagnose (to see all details, use -v option):
[✓] Operation System
[✓] Java
[✓] Android Studio
[✓] Xcode
[✓] Cocoapods
Conclusion:
✓ Your system is ready for Kotlin Multiplatform Mobile Development!
プロジェクトを作る
早速プロジェクトを作ってみましょう。
プロジェクト名やアプリ名、min SDK ver、パッケージ名はなんでも良いのですが、”iOS framework distribution” については、まず最初は “Regular framework” の利用が推奨されているようです。
そうするとプロジェクトのベースとなるコードやファイルが多数生成されます。
“Project” を選択して生成されたファイルを見てみましょう。
このようにいくつかのディレクトリに分かれて管理されていることがわかります。
一番上の階層で見てみると、
- Androidアプリ用ディレクトリ (今回の私の場合は “KMMSampleForAndriod)
- iOSアプリ用ディレクトリ (今回の私の場合は “KMMSampleForiOS)
- shared
という3つのディレクトリに分かれています。
見て想像がつくように、Androidアプリ固有のコード、iOSアプリ固有のコード、そしてsharedで共通のロジックを管理します。
さらに、sharedの中を覗いてみると、
- androidMain
- iosMain
- commonMain
という3つのサブディレクトリがあります。
初期生成されたサンプルコードでは、プラットフォーム固有のバージョンを取得するロジックをinterfaceで切って、ビルド対象によってどの具象クラスを利用するかを判断しています。
// commonMain/.../Platform.kt
package tech.araki.kmmsample
/**
* プラットフォーム固有の情報を取得するためのインターフェース
*/
interface Platform {
val name: String
}
/**
* プラットフォームの情報を取得するための関数。
*
* NOTE: expect修飾子はプラットフォーム固有の情報を得る時に用いる。
* 実際に取得するロジックを含む関数には actual修飾子がつく。
*/
expect fun getPlatform(): Platform
// androidMain/.../Platform.kt
package tech.araki.kmmsample
class AndroidPlatform : Platform {
override val name: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}
actual fun getPlatform(): Platform = AndroidPlatform()
// iosMain/.../Platform.kt
package tech.araki.kmmsample
import platform.UIKit.UIDevice
class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
actual fun getPlatform(): Platform = IOSPlatform()
ここで、見慣れないexpect
/ actual
という関数についている修飾子が出てきます。
これらは、インターフェースと具象クラスの関係のようなもので、具象化するクラスや関数がプラットフォーム依存になる場合は、この修飾子をつけるようです。
ビルドをしてエミュレータで実行してみる
ともかくまずは動かしてみましょう。
まずはAndroidデバイスをターゲットにビルドして、インストールしてみます。
今回は Pixel 5 (API 30) を使っていますが、なんでも良いです。
ビルドが成功しインストールされると、下記のように “Hello, Android 30!” という画面が出てきました。
次にiOS版をビルドしてみましょう。
Build Configurationを iOS のものに変えます。
そうしたらXcodeを立ち上げてから、実行▶︎を押します。
すると、よろしくiOSデバイスのエミュレータが立ち上がり、インストール&実行までしてくれます。
エミュレータの種類やOSバージョンは、”Edit Configuration” から変更できます。
少し手を加えてみる
せっかくなので、外部ライブラリを使って少しコードを改変してみましょう。
KMM含めKotlinのプロジェクトではGradleと呼ばれるビルドツールないし、プロジェクト管理ツールが用いられます。
共通ロジックにおいては、/shared/build.gradle.kts がそれにあたります。
公式に倣って、Kotlinx-datetimeと呼ばれるライブラリを取り込んで、使用してみましょう。
(Kotlinxにこんなライブラリがあったとは知りませんでした…。)
// build.gradle.kts
plugins {
kotlin("multiplatform")
id("com.android.library")
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") // dependencyを追記
}
}
// ...
Sync nowでプロジェクトがリロードされ、Kotlinx-datetimeがプロジェクトで利用できるようになります。
/shared/commonMain/kotlin/[package name]/以下に NewYear.kt を作成して、一つ新しいロジックを書きましょう。
package tech.araki.kmmsample
import kotlinx.datetime.*
/**
* 年明けまでの残り日にちを返す関数
*/
fun daysUntilNewYear(): Int {
val today = Clock.System.todayIn(TimeZone.currentSystemDefault())
val closestNewYear = LocalDate(today.year + 1, 1, 1)
return today.daysUntil(closestNewYear)
}
早速下記修正をGreeting.ktに入れてみましょう。
そしてビルドしてエミュレータで動作させてみると、うまく修正が反映されていそうです。
class Greeting {
private val platform: Platform = getPlatform()
fun greet(): String {
return "Hello, ${platform.name}!" +
"\nThere are only ${daysUntilNewYear()} days left until New Year! 🎆"
}
}
このあとチュートリアルでは、AndroidとiOSそれぞれでViewを修正する流れになるのですが、長くなるのでこの記事ではここまでにします。
ちなみに、iOS用のViewディレクトリ (今回では KMMSamoleForiOS) をXcodeで開くと、プロジェクトとして認識され、そのまま修正に移ることができます。
なので、ViewについてはAndroidはAndroidStudioで、iOSはXcodeで開発を行う、といった形になるようです。
おわりに
今回は、Kotlin Mulitiplatform Mobile (KMM) の触りだけ勉強がてらまとめてみました。
まとめてみた、と言っても公式のドキュメントをそのままやっているだけなので、そこまで価値のある記事ではありあせんが、これは個人的な勉強用メモですので、多めに見てください。
なんとなく概要は掴めたので、いつかKMMでマルチプラットフォームなアプリ開発をやってみたい気持ちはあります。
が、結局作りたいもののアイデアが重要なので、それがいつになるかは分かりません…。
余談
ちなみにこの記事のサムネの右下にいるキャラクターは、Kotlinの公式マスコットです。
絶妙に可愛くないですが、私は好きです。
参考: Kotlin のマスコットが登場! | The Kotlin Blog