📝

【個人開発】Compose Multiplatform で iOS アプリをリリースしました

2024/06/08に公開

はじめに

KotlinConf 2024 で Compose Multiplatform for iOS が晴れてベータ版となったことが発表されました。

https://twitter.com/kotlin/status/1793610157475184798

ベータ版になったことで注目度が高まっている?と思われますが、Android エンジニアが Compose Multiplatform(以下 CMP)を使って iOS アプリをリリースしてみたので、技術的なことや思ったことなどを記事にしたいと思います。

どんなアプリか

こちらです👇

https://apps.apple.com/us/app/フリマメモ-フリマに便利なメモアプリ/id6503227047

フリマの情報をメモできるアプリで、まぁ普通のメモアプリと思っていただければ大丈夫です。
リモートサーバーとの通信はなく、すべて端末内で完結するアプリです。
写真の添付などもできます。

UI はすべて Compose で作成されています。

なぜ Compose Multiplatform を採用したのか 🐦

Android エンジニアなため Swift・SwiftUI ワカラナイ、というのが大前提ですが、Full Compose で作っていた Android 版がすでにあったというのがあります。
また、後述する Jetpack のライブラリの KMP 対応が進んでいることもあり、少ない工数で iOS 版を出せるのでは?と思ったところもあります。

ちなみに Android 版はこちら👇
https://play.google.com/store/apps/details?id=app.tsumuchan.frima_memo&hl=ja

技術的なところ 🧑‍💻

既存改修 or 新規プロジェクト

既存の Android プロジェクトがある状況で CMP を進めるにあたり、

  • 既存のプロジェクトを改修し、CMP 対応していく
  • CMP の新規プロジェクトを作成し、既存のプロジェクトからコードを移植する

の大きく2つの戦略があるのかなと思いますが、今回は後者を選択しました。

理由としては、既存改修は Android 版との互換性を保つのが難しそうな印象があり、さらに不慣れな CMP での開発が進められるか不安だったためです。
なので現状は、Android 版のリポジトリと CMP 版のリポジトリが別々になっており、二重管理になっています。
一応 Android 側のほうも最低限ビルドができる状態をキープしつつ、iOS 側がちゃんと動くように進めたという感じです。

結果として CMP 版もリリースできたので、軌道に乗り始めたら Android 版を CMP のプロジェクトにマージしたいと考えています。

技術スタック

シンプルなメモアプリということもあり、Android 版では込み入ったライブラリなどは使っておらず、主に Jetpack のライブラリを使っていました。

それが功を奏する形で、Jetpack のライブラリが KMP 対応しているものが多かったため、CMP 版でもそのまま使うことができました。

表にするとこんな感じです。

技術 Android 版 CMP 版
データベース Room Room
データストレージ DataStore DataStore
画面遷移 Navigation Compose Navigation Compose + PreCompose
DI Dagger Hilt Koin
画像周り Coil Coil

画面遷移

画面遷移に関してですが、iOS ではお馴染みの操作(と思っている)スワイプバックが現状 Navigation Compose では対応されていないため、PreCompose を併用する形で実装しました。

https://github.com/Tlaster/PreCompose

こんな感じで書けばスワイプバックが実現できます。

NavHost(
    navigator = rememberNavigator(),
    initialRoute = "/home",
    swipeProperties = remember {
        SwipeProperties(
            spaceToSwipe = 40.dp,
        )
    },
    navTransition = remember {
        NavTransition(
            createTransition = slideInHorizontally { it },
            destroyTransition = slideOutHorizontally(animationSpec = tween(easing = LinearEasing)) { it },
            pauseTransition = slideOutHorizontally { -it / 4 },
            resumeTransition = slideInHorizontally { -it / 4 },
            exitTargetContentZIndex = 1f,
        )
    },
) {

DI

DI は Android 版は Dagger Hilt を使っていましたが、KMP 対応してないっぽい?ようなので Koin を使いました。
はじめて Koin を使いましたが、Dagger Hilt と比べると、Koin の方がだいぶシンプルな感じがしました。まぁ Hilt と違ってコンパイル時にチェックされないので、そのあたりは大規模開発だと辛そうな印象もありました。(何度かランタイムクラッシュしました…)

リソース管理

リソース管理も Android の R ファイルと同じ使い勝手のものが CMP にはあるので、スムーズに移行できました。
https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-images-resources.html

良かったこと 💪

思った以上にそのまま動く

今回初の CMP での開発だったので「どうせ iOS だと挙動が違うのが結構あるんじゃないの?」とか疑心暗鬼だったのですが、思った以上にそのまま動いてくれました。
一部 iOS では挙動が違う部分もありましたが、ごく一部でほぼほぼ想定通りという感じ。

パフォーマンス的なところもメモアプリということもあって、普通に触ってみた限り問題ないかなと思っています。

だいたい Kotlin で完結する

ファイル操作やディレクトリ操作などで、iOS 側の処理を書く必要がありますが、だいたい KMP 対応されていました。 NSDocumentDirectoryNSFileManager などの処理も Kotlin で書くことができるのでだいぶ楽でした。

たとえば、iOS 側の Document ディレクトリを取得するのも Kotlin でこんな感じで書けます。

fun getDocumentDirectory(): NSURL {
    val documentDirectory: NSURL? = NSFileManager.defaultManager.URLForDirectory(
        directory = NSDocumentDirectory,
        inDomain = NSUserDomainMask,
        appropriateForURL = null,
        create = false,
        error = null,
    )
    return requireNotNull(documentDirectory)
}

大変だったこと 😅

iOS のビルドが遅い

Hot Reload も現状なく、Kotlin のコードを修正したあとに iOS のビルドをすると、結構時間がかかるのがネックでした。
どうも差分ビルドが機能せず、少しの変更で Kotlin 側のコードをすべてビルドしている雰囲気がありました。そのため、差分ビルドが効く Android の方で確認できるところは確認し、どうしても iOS で確認したいという場面でのみ iOS のビルドをするという感じで進めていました。

iOS 側の処理を多少書く必要がある

だいたい Kotlin で完結する、という言いつつもやはり iOS 側の処理を書く必要がありました。
これはライブラリがまだ充実してないことと関係するのかなと思いますが、広告周りや計測周りでこれというものを発見できず、自分で Swift のコードや SwiftUI のコードを書いたりしました。

Flutter と比較してみて ⚖️

Android 開発がメインなのですが、Flutter 開発も業務・個人で経験があるので、Flutter と CMP を比較してみます。

CMP のメリット

Kotlin で書ける

そのまんまですが、慣れ親しんだ Kotlin で書けるのは大きいです。

Compose の知識がそのまま使える

これもそのまんまですが、Android 版で Compose を使っていると、iOS 版でも Compose を使うことができるのは大きいです。

CMP のデメリット

ホットリロードがない

大変だったことでも記載しましたが、CMP ではビルドが遅く、Flutter のホットリロードのようなものがないのはなかなか不便な印象です。

ライブラリが少ない

Flutter と比べるとやっぱりライブラリが少ないです。
過去に Flutter で開発していたときは Swift のコードを書くことがほとんどなかった記憶ですが、CMP では Swift のコードを書くことがちょいちょいありました。

ネット上の情報が少ない

これもライブラリが少ないと同じ感じですが、まだネット上の情報が少ない印象です。

終わりに

ざっとですが、CMP で iOS アプリをリリースしてみた感想を書いてみました。
思った以上に CMP やれるぞ💪という感じなので正式リリースが待ち遠しいですね。

この記事が誰かの役に立てば幸いです。

Discussion