0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【KMP入門】プロジェクトの作成からサンプルアプリの起動まで

Last updated at Posted at 2025-01-28

Kotlin Multiplatform (KMP) とは

AndroidネイティブアプリとiOSネイティブアプリを同時に開発するためのフレームワークです1

本来はAndroidネイティブ用とiOSネイティブ用で別々のプロジェクトを作り、別々のコードを書き…とするところを、同じプロジェクトとしてまとめられ、一部のコードをKotlinで共通化できるようになります。

本記事は Jetpack ComposeSwiftUIの、少なくともどちらか一方で開発経験のある人を想定としています。

プロジェクトの作成

KMPで新規プロジェクトを作るには Kotlin Multiplatform Wizard を使用します。
https://kmp.jetbrains.com

p2ui9dxdad.png

今回はiOSの「Do not share UI (use only SwiftUI)」を選択します。他の設定は初期状態のままで構いません。

Share UI (with Compose Multiplatform UI framework)」の方はCMPと呼ばれるもので、簡潔に言うとKotlin版Flutterなのですが、こちらはまた別の記事でご説明します。

Fleetのダウンロード

次に、KMP開発元の公式エディターであるFleetをダウンロードします。
https://www.jetbrains.com/ja-jp/fleet/

ダウンロードが完了したらFleetを起動し、「Open...」から先ほど作成したプロジェクトを開きます。

dryniu9jxm.png

KMPプロジェクトの見方

ucnad6c3fg.png

ぐわーっとファイルとディレクトリーが並んでいて驚くと思いますが、重要なのは

  • composeAppAndroidアプリ用のコード(Kotlin) + 設定ファイル
  • iosAppiOSアプリ用のコード(Swift) + 設定ファイル
  • sharedAndroid/iOSで共有するコード(Kotlin) + 設定ファイル

の三つです。それ以外のファイルやディレクトリーは基本的に触らなくても開発できます。

とりあえずアプリを起動してみる

Kotlin Multiplatform Wizard で作成したプロジェクトには簡単なサンプルコードが含まれています。

まずはアプリを動かしてみた方が分かりやすいと思うので、シミュレーターで起動してみましょう。

実行は画面右上にあるRunボタンから出来ます。

ihgdbf2u7b.png

Runボタンを押すと実行するデバイスの選択画面が出ます。
もし緑の雷マークが付いている場合はFleetが準備中なのでしばらく待ちましょう。

595xck5t57.png

普段AndroidやiOSアプリを開発している方は最初から実行する機種名が出ていると思いますが、そうでない場合は「No devices available」という表示になるか、赤い🔴に!マークのアイコンが付きます。
また、iOSアプリとして実行できるのはmacのみです。WindowsやLinuxで開発している方はAndroidのみとなります。

Android、iOSアプリの開発環境の準備はPCを初期化しないと書けないので後日追記予定です

実行に成功すると以下のようなアプリが立ち上がります。

p246uarh3b.png

Click me!」と書かれたボタンをタップするとAndroid、iOSそれぞれに合わせたアイコンが表示されます。

コードを読んでみる

shared

txz2mmu9z2.png

sharedは前述した通り、Android、iOSで共有するコードが格納されています。
普段iOSアプリを開発している方はcommonMain/kotlin/org/example/projectなどの深い階層に驚くと思いますが、Androidアプリ開発ではお馴染みの仕様です。

まずはcommonMain/kotlin/org/example/project/Greeting.ktを読んでみます。

Greeting.kt
package org.example.project

class Greeting {
    private val platform = getPlatform()

    fun greet(): String {
        return "Hello, ${platform.name}!"
    }
}

Greetingというクラスが定義されていて、greet()メソッドではgetPlatform().nameで得られた文字列を返しているのが分かります。
getPlatform()の定義はPlatform.ktにあります。

Platform.kt
package org.example.project

interface Platform {
    val name: String
}

expect fun getPlatform(): Platform

ところが、getPlatform()はありますが実装がありません。

getPlatform()の実装はandroidMainPlatform.android.ktiosMainPlatform.ios.ktにそれぞれあります。

Platform.android.kt
package org.example.project

import android.os.Build

class AndroidPlatform : Platform {
    // OSのAPIレベルを取得
    override val name: String = "Android ${Build.VERSION.SDK_INT}"
}

actual fun getPlatform(): Platform = AndroidPlatform()
Platform.ios.kt
package org.example.project

import platform.UIKit.UIDevice

class IOSPlatform : Platform {
    // OSの種類とバージョンを取得
    override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}

actual fun getPlatform(): Platform = IOSPlatform()

Androidはもちろん、iOSもKotlinでネイティブ機能が呼び出せます。ただし画面に表示させたり、音を鳴らしたりする処理は共有コードとして書くことが出来ず、それぞれのネイティブコード(androidAppiosApp内のコード)として書く必要があります。

このように、KMPの共有コードでは、一つのメソッドをそれぞれのプラットフォーム用に分けて実装することが出来ます。
もちろん、不要であれば全ての共有コードをcommonMain内だけで完結することも可能です。

ちなみにshared/build.gradle.ktsでは、共有コードで使用する外部ライブラリーを追加できます。

iosApp

efm85pnm36.png
.webp)

普段iOSアプリを開発している人にはお馴染みのファイルが並んでいます。

ContentView.swift
import SwiftUI
import Shared

struct ContentView: View {
    // SwiftUIで変数の更新を画面に反映させる、変数の宣言時に@Stateなどを付ける
    @State private var showContent = false
    var body: some View {
        VStack {
            Button("Click me!") {
                withAnimation {
                    showContent = !showContent
                }
            }

            if showContent {
                VStack(spacing: 16) {
                    // Swiftのツバメのアイコンを表示
                    Image(systemName: "swift")
                        .font(.system(size: 200))
                        .foregroundColor(.accentColor)
                    // KMPの共通コードを呼び出して画面に表示させる
                    Text("SwiftUI: \(Greeting().greet())")
                }
                .transition(.move(edge: .top).combined(with: .opacity))
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

二行目のimport Sharedで、KMPの共有コードをインポートしています。

composeApp

8s96ej9xcb.png

src/androidMain以下に、普段Androidアプリを開発している人にはお馴染みのファイルが並んでいます。
src/commonMainには Jetpack Compose に依存しないファイルを入れるようです。
サンプルではcompose-multiplatform.xmlという名前で、アプリに表示させるComposeのアイコンデータ(↑の起動画面にある箱みたいなアイコン)が格納されています。

App.kt
package org.example.project

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.ui.tooling.preview.Preview

import kotlinproject.composeapp.generated.resources.Res
import kotlinproject.composeapp.generated.resources.compose_multiplatform

// @Composableを付けると変数の更新を画面に反映できるようになる
@Composable
@Preview
fun App() {
    MaterialTheme {
        // showContentが変化すると画面が更新される
        var showContent by remember { mutableStateOf(false) }
        Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
            Button(onClick = { showContent = !showContent }) {
                Text("Click me!")
            }
            AnimatedVisibility(showContent) {
                // KMPの共通コードを呼び出して文字列を取得
                val greeting = remember { Greeting().greet() }
                Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
                    Image(painterResource(Res.drawable.compose_multiplatform), null)
                    Text("Compose: $greeting")
                }
            }
        }
    }
}

おわりに

最初にKMPでプロジェクトを作成すると複雑そうに見えますが、実際はAndroid、iOS、共有部分の三つに分かれているだけだとご理解いただけたら幸いです。

次回は簡単なカウンターアプリを作ってみます。
https://qiita.com/hyshu/items/e943fded39ca1d50746b

参考文献

備考

本記事は以下の環境で検証しました

Fleet: 1.44.151
Android Studio: 2024.1 (AI-241.18034.62.2411.12071903)
Xcode 16.2
  1. 更にはデスクトップアプリ、Web、サーバーサイドKotlinも一つのプロジェクトでまとめられ、一部のコードをKotlinで共通化できます

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?