麦芽を支える技術

麦芽(ばくが、英語:malt)とは、麦、特に大麦の種子を発芽させたもので、ビール、ウイスキー、水飴の原料となる。(Wikipediaより)

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

はじめに

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

blog.jetbrains.com

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

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

Getting started - Help | Kotlin Multiplatform Mobile Docs

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

Kotlin Multiplatform Mobile (KMM) とは

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

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

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

kotlinlang.org

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

Kotlin Multiplatform Mobile (KMM) の特徴

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

f:id:asmz0:20200917150126p:plain:w600

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

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

KMMでHello, World!

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

事前準備

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

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

f:id:asmz0:20200917150234p:plain:w600

KMMプロジェクト作成

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

f:id:asmz0:20200917150452p:plain:w600

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

f:id:asmz0:20200917150701p:plain:w600

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

f:id:asmz0:20200917150735p:plain:w600

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

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

f:id:asmz0:20200917150837p:plain:w600

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

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

f:id:asmz0:20200917150929p:plain:w600

ビルド・実行

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

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

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

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

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

f:id:asmz0:20200917151233p:plain:w600

いわゆる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 にはそのプラットフォームに依存したコードを書くことが出来るので、今回の場合はSDKバージョンやOS名、OSバージョンと言ったプラットフォーム依存情報を取得して返却しています。

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

f:id:asmz0:20200917152538p:plain:w600

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

f:id:asmz0:20200917152230p:plain:w600

まとめ

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

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

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

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