Here is a high-quality blog post written for My Core Pick.
一度書いて、より速くリリースする:Kotlin MultiplatformでiOSとAndroidのロジックを統一する
モバイル開発プロセスにおいて、根本的な何かが壊れていると気づいた日のことを覚えています。
チェックアウトフローにバグがありました。
Androidチームは火曜日にそれを修正しました。
iOSチームは木曜日に全く同じバグを修正しました。
私たちは、同じロジックを書き、同じルールを検証し、同じJSONレスポンスを解析するために、2つの異なるチームにお金を払っていたのです。
それは非効率的だと感じました。なぜなら、実際にそうだったからです。
長年、これに対する業界の解決策は、FlutterやReact Nativeのような「クロスプラットフォーム」フレームワークでした。
しかし、それらはしばしばネイティブのユーザーインターフェース(UI)を犠牲にしたり、処理速度を低下させるパフォーマンスブリッジに対処することを意味しました。
そして、Kotlin Multiplatform (KMP)が登場しました。
それは異なる約束をしました。「ロジックを共有し、UIはネイティブのままにする」というものです。
今日は、なぜKMPがモダンなモバイルアーキテクチャにおける私の「Core Pick(核心的な選択)」なのか、そして、どのようにしてコードを一度書き、これまで以上に速くリリースできるようにするのかについてお話しします。
「一度書けば、どこでも動く(Write Once, Run Everywhere)」の問題点

長い間、モバイル開発の聖杯は「どこでも動く単一のコードベース」でした。
React NativeやFlutterのようなフレームワークは、独自のUIをレンダリングすることでこれを解決しようとします。
これらは基本的に、「ネイティブのiOSやAndroidのボタンは無視して、我々のボタンを使ってください」と言っているようなものです。
これは多くのアプリでうまく機能します。
しかし、時々、少し違和感を覚えることがあります。
アニメーションが完全に適切でなかったり、フレームワークがまだサポートしていない新しいiOS機能が登場したりします。
私はユーザーエクスペリエンス(UX)に関しては常に純粋主義者でした。
iOSアプリは、SwiftUIやUIKitを使用して、100% iOSアプリのように感じさせたいのです。
Androidアプリは、Jetpack Composeを使用して、100% Androidアプリのように感じさせたいのです。
しかし、データ検証ロジックを2回書きたくはありません。
ネットワーキング層を2回書きたくはありません。
ここでKotlin Multiplatformがゲームを変えます。
これは「一度書けば、どこでも動く(Write Once, Run Everywhere)」ソリューションになろうとはしていません。
これは「一度ロジックを書けば、どこでも動く(Write Logic Once, Run Everywhere)」ソリューションなのです。
KMPの実際の仕組み(その内部)

KMPに不慣れな場合、そのアーキテクチャは少し魔法のように見えるかもしれません。
しかし、実際には非常に標準的なプログラミングの概念に基づいています。
KMPプロジェクトには、Shared Module(共通モジュール)があります。
このモジュールには純粋なKotlinコードが含まれています。
ここが、「ビジネスロジック」を置く場所です。
ビジネスロジックとは何か?
私がビジネスロジックと言うとき、それはアプリケーションの頭脳を意味します。
これには、バックエンドへのAPI呼び出しが含まれます。
ローカルデータベースへのデータのキャッシュも含まれます。
ユーザー入力の検証(メールアドレスが有効かどうかのチェックなど)も含まれます。
JSONデータをアプリが使用できるオブジェクトに解析することも含まれます。
コンパイラの魔法
プロジェクトをコンパイルするとき、KMPは素晴らしいことを行います。
Androidアプリの場合、通常通りKotlinコードをJavaバイトコードにコンパイルします。
しかし、iOSアプリの場合、同じKotlinコードをAppleフレームワークにコンパイルします(LLVMを使用)。
Xcodeにとって、共有されたKotlinコードは単なるObjective-CやSwiftのフレームワークに見えます。
あなたはそれをインポートし、関数を呼び出します。
iOS開発者は、それを使用するためにKotlinの書き方を知る必要さえありません。
彼らはただ fetchUser() という関数を見て、それを呼び出すだけです。
秘密兵器:ExpectとActual

KMPの最も強力な機能の一つは、expect と actual のメカニズムです。
時々、共有コード内でプラットフォーム固有のAPIにアクセスする必要があります。
例えば、UUIDを生成する必要があるかもしれません。
AndroidはこれをiOSとは異なる方法で処理します。
共有コード内で、expect 関数を定義できます。
それは次のようになります: expect fun randomUUID(): String
これは基本的に、「文字列を返す関数があるはずだが、それがどのように機能するかはまだ知らない」と言っているのです。
そして、Androidのソースセット内で、Javaライブラリを使用して actual 実装を書きます。
iOSのソースセット内では、AppleのFoundationライブラリを使用して actual 実装を書きます。
これにより、高レベルのロジックを共有しながら、必要なときにはいつでもネイティブの機能を利用することができます。
これは、他のクロスプラットフォームフレームワークがしばしば持つ「ブラックボックス」の制限を取り除きます。
私のCore Pick:KMPテックスタック
KMPに飛び込むと決めたら、適切なライブラリが必要です。
RetrofitやRoomはAndroid固有のJavaライブラリであるため、そのままでは使えません。
マルチプラットフォーム互換のライブラリが必要です。
堅牢なKMPアプリケーションのための、私の「Core Pick」スタックは以下の通りです。
ネットワーキング:Ktor
KtorはマイクロサービスやWebアプリケーションを作成するための非同期フレームワークですが、素晴らしいHTTPクライアントも作成します。
これはJetBrains(Kotlinの作成者)によって構築されており、完全にマルチプラットフォーム対応です。
ヘッダー、認証、シリアライズをiOSとAndroid全体でシームレスに処理します。
データベース:SQLDelight
これは私が最も気に入っているデータベースツールです。
SQLDelightは、SQL文から型安全なKotlin APIを生成します。
コンパイル中にスキーマを検証します。
SQLクエリにタイプミスがあれば、コードはコンパイルされません。
AndroidとiOSの両方でSQLite上で動作し、データ層を極めて強固なものにします。
依存性注入:Koin
依存性注入は、マルチプラットフォーム環境では扱いにくい場合があります。
Koinは、KMPと非常に相性の良い軽量な注入フレームワークです。
読みやすい特定のDSL(ドメイン固有言語)を使用します。
これにより、データベースやネットワーククライアントを共有ViewModelに直接注入できます。
並行処理:Kotlin Coroutines
iOSとAndroidでのスレッド処理は、かつてコールバックとデリゲートの悪夢でした。
Kotlin Coroutinesを使用すると、非同期コードを同期コードのように書くことができます。
バックグラウンドスレッドで複雑な操作を実行し、UIをブロックすることなく結果をメインスレッドに返すことができます。
重要なのは、これがSwiftのasync/awaitパターンと相互運用できるため、iOS開発者が自然に利用できることです。
チームにとっての現実的なメリット
なぜ会社でこれを推奨すべきなのでしょうか?
それは、スピード、一貫性、そして精神衛生(Sanity)の3点に帰結します。
1. イテレーションのスピード
新しい機能を追加するとき、ロジックを一度書きます。
単体テストを一度書きます。
UIを2回構築するだけで済みます。
これにより、チームが慣れてくれば、通常30〜40%程度の開発時間の大幅な短縮が可能になります。
2. 一貫性は王様(Consistency is King)
先ほどバグ修正について触れました。
KMPを使用すると、共有モジュール内のロジックのバグを修正すれば、両方のプラットフォームで即座に修正されます。
iOSアプリとAndroidアプリが全く同じように動作することを保証できます。
それらは同じ方法で価格を計算します。
同じ方法でリストを並べ替えます。
これにより、QAチームの認知負荷を大幅に軽減します。
3. 開発者の精神衛生
Android開発者はすでにKotlinが大好きです。
それは現代的で、表現力豊かで、安全な言語です。
iOS開発者は、正直なところ、Swiftを離れるのをためらうことがよくあります。
しかし、KMPはUIをSwiftUIに任せているため、iOS開発者はコントロールを失っているとは感じません。
彼らは依然として美しく、ネイティブなインターフェースを構築できます。
JSONを解析するための退屈なボイラープレートコードを書く必要がなくなるだけです。
始め方:導入のための戦略
では、どう始めればよいでしょうか?
明日、アプリ全体を書き直さないでください。
それは大失敗の元です。
KMPは段階的に導入できるように設計されています。
小さな、独立したモジュールから始めてください。
おそらくそれは分析ロジックかもしれません。
あるいは、「ニュースフィード」のようなシンプルな機能かもしれません。
ステップ1:セットアップ
JetBrainsが提供するKotlin Multiplatform Wizardを使用してください。
フォルダ構造とGradle設定を生成してくれます。
これにより、設定に関する何時間もの頭痛の種を解消できます。
ステップ2:モデルの移行
データモデル(基本的なクラス)を共有モジュールに移動することから始めます。
これにより、両方のアプリが同じ言語を話すことが保証されます。
ステップ3:ネットワーキングの移行
モデルが共有されたら、API呼び出しを移動します。
Retrofit (Android) と Alamofire (iOS) を共有モジュールのKtorに置き換えます。
データをネイティブUI層に渡します。
最後に
モバイル開発の状況は変化しています。
私たちは、iOSとAndroidが完全に異なる世界であるというサイロ化されたアプローチから移行しつつあります。
また、Webベースのクロスプラットフォームツールの「妥協」も過去のものになりつつあります。
Kotlin Multiplatformはスイートスポットを突いています。
それは重要な部分(UI)におけるプラットフォームの違いを尊重します。
それは重要な部分(ロジック)における類似性を統一します。
より速くリリースし、バグを減らし、ネイティブのルックアンドフィールを維持したいなら、KMPこそが進むべき道です。
これは間違いなく、私の開発者ツールキットにおけるCore Pickです。
次の機能で試してみてください。そうすれば、同じコードを2回書く作業には戻りたくなくなるはずです。