Kotlin Multiplatform Mobile (KMM)を使ったモバイルアプリ開発
はじめに
KINTOテクノロジーズでグローバルグループのモバイルアプリ開発を担当している、謝堯(Yao Xie)、方茂碩(Mooseok Bahng)です。
現在、Global KINTO App というアプリの開発を担当しています。Global KINTO App (GKA)は「世界中のKINTOサービスを1つのアプリでつなぐ」というコンセプトを持たせたモバイルアプリです。現時点ではタイとカタールのKINTOサービスが実装されています。
既存のアプリのかわりになるプロジェクトを進めている中で、Kotlin Multiplatform Mobile (以下 KMM)の導入を決めましたので、今回お話します。
KMMの導入を決めた背景
KMMの導入を決めた背景には次のような課題がありました。
- どうしてもiOSとAndroidの間に Business Logicの実装の差が発生する。
- 開発チームが物理的に2ヶ所に別れていて、開発の効率が落ちる場合がある。-> チームを KMMとNativeに分けることで改善できると思った。
- 開発リソースが限られているため、効率的な開発体制を構築したい。
その結果、KMMを検討することになりました。
Kotlin Multiplatform Mobile (KMM)とは
KMMは iOS, Androidアプリを開発するためのSDKで、基本言語は Kotlinで、Cross-Platformと Nativeアプリのいいところ取りをしています。
KMMで共通のビジネスロジックを開発して、プラットフォーム依存性がある UIはそれぞれ Nativeで開発できます。
個人的にはそれぞれのOSに最適なUIを提供することが一番いいUI/UXだと思っています。
KMMはUIは基本それぞれの Nativeで開発するため、KMMはUIは基本それぞれの Nativeで開発するため、UI/UXの最適化ができ、iOS, Androidとの依存性が少なく、バージョンアップの影響もほとんどないと思っています。
KMMはまだまだ新しい技術で、十分に成熟してないですが、近年いろいろな会社で使われている状況です。
Kotlin Multiplatform Mobile (source: https://kotlinlang.org/lp/mobile)
Architecture
KMMについて説明する前に、現在開発チームで採用しているアーキテクチャーについて説明します。
基本的にMVVMを採用して開発しています。KMMを導入しても基本この方針には変更はありません。
ここで、どこまでKMMに含めるかが悩みでした。選択肢は三つぐらい考えられます。
KMM | Native | |
---|---|---|
Option 1 | Repository, Usecase, View Model | UI |
Option 2 | Repository, Usecase | View Model, UI |
Option 3 | Repository | Usecase, View Model, UI |
色々試してみましたが、今のところはView ModelまでKMMにする方向に進んでいます。View Modelは除外することも検討してみましたが、せっかくKMMを導入したのに View Modelを別々にする理由が見つかりませんでした。データをリストに表示するだけなどの簡単なフィーチャーについては特にそうです。
これから複雑な機能が追加されると、View Modelを別にする必要があるかもしれません。その時は一部だけView Modelを別々にしたりすることも考えられます。
iOSのCodebaseはかなりコンパクトになりました。Domain LayerとView ModelまでKMMを使っているので、UIとプラットフォーム依存のあるハードウェア関連の機能だけになり、ソースコードの量は半分以下になるのではと思っています。。
下記はFAQリストを表示する簡単な画面のiOS側の実装になります。共通のUI Utility Classを除くとこれで終わりです。
struct FaqView: View {
private let viewModel = FaqViewModel()
@State var state: FaqContractState
init() {
state = viewModel.createInitialState()
}
var body: some View {
NavigationView {
listView()
}
.onAppear {
viewModel.uiState.collect(collector: Collector<FaqContractState> {
self.state = $0
}
) { possibleError in
print("finished with possible error")
}
}
}
private func listView() -> AnyView {
manageResourceState(
resourceState: state.uiState,
successView: { data in
guard let list = data as? [Faq] else {
return AnyView(Text("error"))
}
return AnyView(
List {
ForEach(list, id: \.self) { item in
Text(item.description)
}
}
)
},
onTryAgain: {
viewModel.setEvent(event: FaqContractEvent.Retry())
},
onCheckAgain: {
viewModel.setEvent(event: FaqContractEvent.Retry())
}
)
}
}
メリット
- Single Codebase
iOS, Androidのネットワーキング、データストレージ、ビジネスロジックなどを Single Codebaseで管理することができます。
- Consistency
ビジネスロジックを共有するため、基本的に同じUXを提供することができます。
- Efficiency
KMMの導入によって効率的な開発ができるようになりました。半分に近いタイムコストをカットすることによって、その分、ソースコードの最適化やビジネスの展開に時間を使うことができるようになりました。
- Expandable
iOS, Androidだけではなく、必要によっては他のプラットフォームにも簡単に拡張することができます。
デメリット
- iOSのデバッグは別途のpluginのインストールが必要となります。
- XCframeworkを適用する場合、Apple Siliconの Macで Simulatorを使うとarm64を参照するため、エラーになります。これは KMM SDK側の修正が必要だと思いますが、暫定的には excluded architectureに arm64を追加するか、x-codeを rosettaモードで実行すると simulatorも使えるようになります。
iOSへの配布方法
Build & Sourcesets
Build XCFrameworks
KMMの今までのiOSへの配布は Universal (FAT) frameworkが基本でした。最近、やっと公式にXCFrameworkをサポートすることになりましたので、XCFrameworkを採用する予定です。
その他
KMMと直接関係はないですが、下記の新しい技術も導入を検討しています。
Ktor
- 両方のClientに対して同じConfigurationをセットすることができます。
- API Requestコードは共通です。
- iOS, Androidのエンジンは別々ですが、追加のコードはいらないです。
Apollo Client
既存のプロジェクトではGraphQL APIも一部採用しています。GraphQLのために Apollo Clientの導入を検討しています。
Backendの schema.graphqlsを使い、Queries.graphqlを作成するだけで、Models, Adapters, Queriesが自動生成されます。
MMKV
MMKVはモバイル key-value storage frameworkです。もちろん、Android, iOSを含めて、マルチプラットフォームに対応しています。
MMKV-Kotlinで MMKVを簡単に私たちのプルジェクトにインテグレートすることができ、shared moduleで key-value storageを管理することができるようになります。
Performance comparison on Android
Performance comparison on iOS
今後の展開
Fast growing KMM ecosystems
KMMは JetBrainsによって開発されています。Android Studioでシームレスに開発でき、Xcodeも一部対応しています。
開発者の中で広がってきており、KMMのためのオープンソースライブラリがたくさん存在します。
Cross-platform UI
Touchlabはすでに Compose UIを iOS、Android両方に使う実験を開始しています。
@Composable
internal actual fun PlatformSpecificSettingsView(viewModel: SettingsViewModel) {
IconTextSwitchRow(
text = "Use compose for iOS",
image = Icons.Default.Aod,
checked = viewModel.observeUseCompose,
)
Divider()
}
近いうちにKMMは公式に cross-platform UIをサポートするようになるかもしれません。
まとめ
ここまでKMMの導入について説明しました。
KMMの導入によって下記の改善が期待できるようになりました。
- iOSとAndroidの間のビジネスロジックの実装の差を最小限に抑制することができる。
- 必要によっては、開発チームの構成の最適化ができる。
- ある程度開発工数を減らすことができる。
まだまだKMM導入の初期段階なので、課題にたくさん直面し、どんどんノウハウが蓄積するので、進捗があり次第また共有します。
ありがとうございました。
関連記事 | Related Posts
We are hiring!
【プロジェクトマネージャー】モバイルアプリ開発G/大阪
モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。
【iOSエンジニア】モバイルアプリ開発G/東京
モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。