Kotlin Multiplatform Mobile (KMM) ことはじめ 〜Hello, World!〜

9 min読了の目安(約5800字TECH技術記事

この記事は、とりあえずZennの書き心地とかを試してみたく、先日自分のブログに書いた記事を移行したものです。もしこっちがいい感じだったら今後乗り換えるかもしれません。

はじめに

2020/8/31にKotlin Multiplatform MobileがAlpha版へ移行したというニュースが発表されました。

https://blog.jetbrains.com/kotlin/2020/08/kotlin-multiplatform-mobile-goes-alpha/

私はもともとKotlin Multiplatform Project構成で個人アプリの開発を試していたこともあり、今回のこの新たな発表について割と強い関心を持っていたので、早速触ってみることにしました。

ちなみに、今回やったのはなんら難しいことではなく、公式のGetting startedの序盤部分です。

https://kotlinlang.org/docs/mobile/getting-started.html

詳しいことは全て↑こちらに書いてあります。興味がある方はぜひ読んでみてください。

Kotlin Multiplatform Mobile (KMM) とは

Kotlinは現状以下のプラットフォームに対応しています。

  • Kotlin/JVM (Android, Server)
  • Kotlin/Native (iOS, macOS, Windows, Linux etc...)
  • Kotlin/JS

これら総称してKotlin Multiplatform、このような複数プラットフォームに対応するプロジェクト構成はKotlin Multiplatform Project(Kotlin/MPP)と呼ばれていました。

今回発表された「Kotlin Multiplatform Mobile (KMM) 」は、このうち「Android」「iOS」つまりモバイルアプリのクロスプラットフォーム開発に特化したSDKという理解です。

https://kotlinlang.org/lp/mobile/

これまでもMPP構成でAndroid/iOSクロス開発は行うことができましたが、KMMはこのユースケースに特化することでモバイルアプリ開発に便利な機能やテンプレートをIDEと統合し、これまでより簡単にプロジェクト構成が作れるようになっています。

Kotlin Multiplatform Mobile (KMM) の特徴

KMMは、他のクロスプラットフォーム環境とは異なり、アプリ全てのソースコードを共通化するという考えではありません。

このように、各OSのUI部分に関するところはそれぞれネイティブ実装し、ビジネスロジックなどUIに関わらない部分について共通化する、というアプローチとなっています。

この共通化された部分はライブラリ・frameworkとして各プラットフォームへ提供され、各プラットフォーム側ではそのライブラリ・frameworkを利用して開発する、という形になります。

KMMでHello, World!

それでは早速Android/iOSアプリを作っていきましょう。

事前準備

開発には以下の環境が必要となります。

  • Android Studio 4.1以降推奨
    • 4.0系でも動作するが一部機能制限あり
  • Android Studio Kotlin PlugIn 1.4.0以降
  • Android Studio Kotlin Multiplatform Mobile PlugIn
  • Xcode 11.3以降

KMMプラグインはこんな感じでAndroid Studioから簡単に導入することができます。

KMMプロジェクト作成

新規KMMプロジェクトはAndroid Studioのメニューの「File」→「New」→「New Project..」から作成できます。

プロジェクト基本情報設定

Android、iOS、共有モジュールの名前の設定

ちなみに上記はAndroid Studio 4.1の画面ですが、4.0.1だとこの各モジュール名は変更できず、デフォルトで「androidApp」「iosApp」「shared」の命名となるようです。

そして作成されたプロジェクト構成は以下のようになります。

見ての通り、KMMプロジェクト内にAndroidアプリ、iOSアプリ、共通モジュールそれぞれのプロジェクトが内包された形となります。

ちなみに、上記「iosApp」配下は通常のXcodeプロジェクト構成なので、このように普通にXcodeで開くことができます。

ビルド・実行

それではこの新規作成したプロジェクトのソースは特に変更せず、そのままビルドしてみます。

Androidはもちろんですが、KMMプラグインを導入していればiOSアプリもAndroid Studioでビルドすることが可能です。

それぞれ以下の状態で「▷」ボタンでビルド開始します。

  • Androidアプリビルド

  • iOSアプリビルド(using Android Studio)

ちなみに、ビルド中のログをみていると、iOSアプリのビルドの場合は裏でXcodeが何らか動いてることがわかります。

ビルドが完了すると、以下のような感じでAndroid/iOS両アプリが起動します。

いわゆるHello, World!ですね。

生成コード説明

実際に生成されたコードがどうなっているか、軽く追ってみます。

以下がAndroid/iOS両プロジェクトで実際に画面にテキストを表示している部分の抜粋です。

androidApp/~

fun greet(): String {
    return Greeting().greeting()
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        :
        val tv: TextView = findViewById(R.id.text_view)
        tv.text = greet()
    }
}

iosApp/~

func greet() -> String {
    return Greeting().greeting()
}

struct ContentView: View {
    var body: some View {
        Text(greet())
    }
}
:

※ iOSの生成コードはSwiftUI...!

どちらも Greeting().greeting() でテキストを出力しています。この Greeting().greeting() が共通モジュールで定義されているものです。

この共有モジュールはこのような形で定義されています。

shared/src/commonMain/~

class Greeting {
    fun greeting(): String {
        return "Hello, ${Platform().platform}!"
    }
}
expect class Platform() {
    val platform: String
}

見ての通りStringを返却するだけですが、Helloの後に続くプラットフォーム情報はここでは実装されておらず、 expect クラスとしてインタフェースのみ実装されています。

これはKotlin Multiplatformの特徴の一つですが、共通モジュール内で各プラットフォームに依存する処理が必要な場合、このように共通モジュール側には expect としてインタフェースのみ定義し、それに対応する actual を各プラットフォーム側で実装する、という方法で対応します。

で、今回の Platform().platformactual は以下のように定義されています。

shared/src/androidMain/~

actual class Platform actual constructor() {
    actual val platform: String
        = "Android ${android.os.Build.VERSION.SDK_INT}"
}

shared/src/iosMain/~

import platform.UIKit.UIDevice

actual class Platform actual constructor() {
    actual val platform: String
        = UIDevice.currentDevice.systemName()
           + " " + UIDevice.currentDevice.systemVersion
}

※ KotlinでUIKitを書くという違和感...!

actual の実装がある shared/src/androidMain/~shared/src/iosMain/~ ではそれぞれのプラットフォームに依存したコードを書くことが出来るので、今回の場合はSDKバージョンやOS名、OSバージョンと言ったプラットフォーム依存情報を取得して返却しています。

雑に図を書くとこんな感じです。

その結果、以下のように各OS独自の情報が含まれた実行結果となったわけですね。

まとめ

以上がKMMのHello, World!でした。実際やったことはKMMプロジェクトを新規作成しただけなんですけどね。

要点をまとめると以下のような感じです。

  • KMMとはKotlinでAndroid/iOSクロスプラットフォーム開発を行うためのSDK
  • 主にUIに寄らないビジネスロジック部分をAndroid/iOSで共通化
  • モジュールとしては共通化したいが、プラットフォーム依存などによりどうしても一部共通化しづらい部分がある場合、expect, actualで独自実装が可能

KMM以前は自身で共通モジュールの build.gradle を用意し、それぞれのプラットフォーム用ビルドを構成する必要があり、そもそも共有モジュールのビルドで結構詰まったりすることが多かったんですが、その辺が自動生成され、あまり意識することなくアプリのビルドまで出来るようになっていたので、だいぶ手軽にKMMに取り掛かれるようになった印象です。

引き続きKMMでいろんなこと試していこうと思います〜