はじめに
基本、公式のチュートリアルをなぞってます。
https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html
公式チュートリアルは全文英語の記事となっているので、英語苦手だよーって方とかは参考にしていただけると幸いです。
いままでアプリ開発とかをしてこなかった方でも理解できるよう、深くまでは掘らず比較的ざっくりとした感じの記事構成となっていますので、あらかじめご了承ください。
前提
- kotlin multiplatformの環境構築が完了していること
https://qiita.com/atsu123456789/items/147e9e03d2018b20841b
ロジックの実装
共通処理について
まずはGreetingKMP/composeApp/src/commonMain/kotlin/com/jetbrains/greeting/Greeting.kt
の中身を書き換えてみます。
package com.jetbrains.greeting
import kotlin.random.Random
class Greeting {
private val platform: Platform = getPlatform()
fun greet(): String {
val firstWord = if (Random.nextBoolean()) "Hi!" else "Hello!"
return "$firstWord Guess what this is! > ${platform.name.reversed()}!"
}
}
ここでパスやファイル名はproject IDや project Nameによって決まっているものなので、公式チュートリアルどおり進めていれば同じになるはずです。
ここで一回、エミュレータで実行してみると以下のようになりました。
androidとiOSで同じソースの内容が、反映されたことが分かりました。すごいぞ!
特定OSでの処理について
OSによって固有の処理を持たせたい場合はandroidMain
、iosMain
の中に処理を記載します。
ただ、commonMain
でinterfaceクラスを実装し、その実体クラスを使っての実装ができ、今回はプロジェクトの初期実装の処理の中身を追っていこうと思います。
まずGreetingKMP/shared/src/commonMain/kotlin/com/jetbrains/greeting/Platform.kt
の中身を見てみます。中身は下記の通りです。
package com.jetbrains.greeting
interface Platform {
val name: String
}
expect fun getPlatform(): Platform
ここではinterfaceクラスを作成していることが分かります。
ではこのinterfaceクラスを継承してどこで実体クラスを作成しているかというと、それがsharedディレクトリ内のandroidMain
とiosMain
になります。
GreetingKMP/shared/src/iosMain/kotlin/com/jetbrains/greeting/Platform.ios.kt
というファイルがあるので、その中を見てみます。
package com.jetbrains.greeting
import platform.UIKit.UIDevice
class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
actual fun getPlatform(): Platform = IOSPlatform()
class IOSPlatform
はinterface Platform
を継承していることが分かりますね。
同様にGreetingKMP/shared/src/androidMain/kotlin/com/jetbrains/greeting/Platform.android.kt
も見てみましょう。
package com.jetbrains.greeting
import android.os.Build
class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"
}
actual fun getPlatform(): Platform = AndroidPlatform()
ここでもiOSと同様、class AndroidPlatform
がinterface Platform
を継承していることが分かります。
なので、両OSで同じ処理を実装したいときで、かつOSによって処理を分ける必要がある処理の場合、以下のような流れで実装することになるのかなと思いました。
-
commonMain
配下?にinterfaceクラスを作成 - そのinterfaceクラスを使って画面描画できるよう実装する(チュートリアルの例でいうと
App.kt
やGreeting.kt
がそれに該当する) - 手順1で作成したinterfaceクラスを継承した実体クラスを
androidMain
、iosMain
内でそれぞれ実装する
簡単な処理だけならば上記のような流れになるかなーと思いました。
UIの実装
次にそれぞれのOSでUIの変更についての実装をしていきます
※ここではUIは共通のフレームを使用するのではなく、android、iOSで独自の実装をしていきます。
そのため、wizardでプロジェクトを作成する際にiOSの欄のラジオボタンで「Do not share UI (use only SwiftUI)」を選択しています。
https://kmp.jetbrains.com/?_gl=1*xnvfpr*_gcl_au*NDY4OTY0ODc2LjE3MjI5NDgwMjU.*_ga*ODA3MzE5NTEzLjE3MjI5NDgwMTI.*_ga_9J976DJZ68*MTcyMzU2NTI5Ny44LjEuMTcyMzU2ODMzMS41Ny4wLjA.
Android
まずはGreeting.kt
でgreetingメソッドが呼ばれた際にStringのリストを返すように実装します。
package com.jetbrains.greeting
import kotlin.random.Random
class Greeting {
private val platform: Platform = getPlatform()
fun greet(): List<String> = buildList {
add(if (Random.nextBoolean()) "Hi!" else "Hello!")
add("Guess what this is! > ${platform.name.reversed()}!")
}
}
Androidの場合、このメソッドを呼び出している箇所は以下になります。
GreetingKMP/composeApp/src/androidMain/kotlin/com/jetbrains/greeting/App.kt
そのため、このファイルを修正していきます。
package com.jetbrains.greeting
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.ui.tooling.preview.Preview
@Composable
@Preview
fun App() {
MaterialTheme {
val greeting = remember { Greeting().greet() }
Column(
modifier = Modifier.padding(all = 20.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
greeting.forEach { greeting ->
Text(greeting)
Divider()
}
}
}
}
やっていることとしては、Column
を使ってリストのようなUIにして、Greeting().greeting()
で取得したStringの配列をforEachでそれぞれ取得し、Text(greeting)
で文字列を表示しているだけのシンプルなコードになります。
※ライブラリなどは実装に合わせて適宜入れてください。
この状態でandroidエミュレータを実行すると以下のようになりました。
Greeting.kt
で生成したStringの配列がそのままリストとして生成されたことが分かります。
iOS
つづいてiOSでも同様の実装を行なっていきます。
Greeting.kt
はAndroidのところで修正したものをそのまま使用して、iOSもAndroidと同様にリストのUIで表示していきます。
iOSのUIのロジックについては以下で持っているので、そこを修正していきます。
GreetingKMP/iosApp/iosApp/ContentView.swift
import SwiftUI
import Shared
struct ContentView: View {
let phrases = Greeting().greet()
var body: some View {
List(phrases, id: \.self) {
Text($0)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
iOSもAndroidと同様でやっていることとしては、Greeting().greeting()
でStringの配列を取得し、それをList()を使用して、Text表示しているだけです。
この状態でエミュレータ起動すると以下のようになりました。
iOSでもAndroid同様、Greting().greeting()
で取得したStringの文字列をリストとして画面表示することができました。
さいごに
一旦今回はここまでとさせていただきます
他のクロスプラットフォームを使用したことがないのでわからないのですが、iOS、androidでそれぞれのOSに適したUIを使用できることは良いなーと思いました。
swiftUIでiOSのUIを綺麗に描ける一方で、まだまだswiftUIではできないこともあるので、storybookとかは使用できないのかなーとか思ってたりですが、swiftUI、KMPでこれから色々できるようになっていくことを考えると楽しみですね!
次の記事でサードパーティのライブラリの追加等について書ければと思いますー
ここまで読んでくださりありがとうございました!
参考文献