React Native か Swift・Kotlin か:私たちがスタックを選ぶ基準

Software Mansion が、スタック選定の背景にある 5 つの問いと 3 つのアプローチを解説し、Gobi が React Native からネイティブの iOS / Android へ画面ごとに移行した理由を説明する。

日本語
コピー
浅色像素风背景上,Swift、Kotlin 与 React Native 三个图标被彩色线条牵引,汇成一支箭头指向右方「application」字样

React Native とネイティブのどちらを選ぶかは、一度で決まるものではない。適した技術スタックは、プロダクトが何を実現するのか、どれだけ早くリリースするのか、チームがどう成長する見込みなのかによって変わる。性能、予算、プラットフォーム機能の可用性、長期的な保守性——これらの重みは、プロダクトのライフサイクルの各段階で異なる。

私たちは React Native、Swift、Kotlin、そしてハイブリッドな構成でモバイルアプリを作ってきた。コード共有の効率と、ネイティブ開発ならではの細かな制御の間でどうバランスを取るかは、よく分かっている。

以下では、選定前に私たちが問う5つの質問、そこから絞り込む3つの選択肢、そしてその答えが Gobi でどう変わっていったかを説明する。Gobi は場所やイベントを見つけるソーシャルアプリで、私たちは React Native からネイティブの iOS と Android へ移行した。

選定前に見るもの

こうしたトレードオフを地に足のついた議論にするため、私たちは5つの質問を順に確認する。

  • 何を、いつまでに届けるのか? 今回のリリース範囲、締め切り、予算を、各選択肢に必要な作業量と突き合わせる。React Native と EAS は確かに配布を楽にする。互換性のある JavaScript やアセットの更新は OTA で配信できる。ただしネイティブ側の変更にはリビルドが要る。見積もりには、フレームワーク側の回避策と想定される移行のコストも含める。
  • 今のアプリはどこで詰まっているのか? クラッシュ、フリーズ、低いフレームレート、もたつく応答を洗い出す。移行を勧める前に、問題が UI のレンダリング、状態管理、データ読み込み、バックエンドのどこにあるのかを突き止める。肝心なのは、それが一度きりの不具合なのか、アーキテクチャを見直すべき持続的な問題なのかだ。
  • 中核となる機能は何で、どう動くのか? 機能の複雑さ、データ量、頻繁な操作や新着データが協調の必要な UI 更新をどう引き起こすかを見る。アニメーションの各フレームでどれだけの処理を完了させる必要があるか、操作を滑らかに保つために何をバックグラウンドスレッドへ移すべきかも考える。
  • プロダクトはどこへ向かうのか? 新機能が共有 UI の価値を高めるのか、それともネイティブの制御をより多く必要とするのかを知りたい。新機能が共有の画面や操作パターンを再利用できるなら、React Native は依然として魅力的だ。SwiftUI と Jetpack Compose で画面を分けても、ビジネスロジックは Kotlin Multiplatform で共有できるし、Swift SDK for Android を使って互換性のある Swift モジュールを再利用することもできる。
  • 今あるものは何で、今後は誰が保守するのか? 既存のコード、統合の仕方、チームの経験を見る。大規模な React Native アプリなら、狙いを絞ったネイティブ開発が助けになることがある。小規模なプロジェクトでも問題が深刻なら、早めに作り直すか移行する価値があるかもしれない。どちらの場合も、チームが無理なく維持できるアーキテクチャが必要だ。

これらの問いが、プロダクトのワークロード、リリース目標、チームの保守能力に合った選択肢を選ぶ助けになる。

React Native か、ネイティブか、その両方か——3つの選択肢

React Native を土台にする

React Native は iOS と Android で UI とアプリロジックを共有でき、そのエコシステムがアイデアからリリースまでの道のりを短くする。既製のライブラリと Expo の SDK でよくある機能を実装しやすくなり、Fast Refresh と React Native DevTools が素早い反復とデバッグを支える。Expo の EAS を使えばビルドと配布が簡単になり、EAS Update は互換性のある変更を JavaScript コードとアセットへ OTA で配信できる。ネイティブ側の変更にはリビルドが必要だ。2つのプラットフォームに共通点が多く、チームが頻繁な更新から恩恵を受けられる場合に、特に向いている。

React Native とネイティブモジュール

ハイブリッドな構成は、上記のワークフローと、一部をネイティブコードで実装した機能を組み合わせる。完全にネイティブコードで実装した機能は、データ読み込み、状態、UI を自前で管理しつつ、外部には限られたコマンドと結果のインターフェースだけを公開する。共有 UI の外側に専用の体験を備えたプロダクトに向いている。たとえばインタラクティブな地図、メディア処理、独自のジェスチャーなどだ。機能ごとに適した実装を選べるうえ、全体としてはプロダクトの長期的なアーキテクチャの中に収まる。

完全ネイティブのアプリ

ネイティブアプリはプラットフォームの API を直接使え、並行処理、バックグラウンド処理、アニメーションのためのツールも利用できる。ハイブリッドな機能でもこれらは使える。完全ネイティブのアーキテクチャなら、JavaScript からネイティブへのブリッジ層に頼らず、アプリ全体でそれらを統一的に扱える。密結合で要求の厳しい操作がプロダクトの大きな部分を占め、データ処理、レンダリング、ユーザー入力の扱い方にチームが柔軟さを必要とする場合に、特に向いている。ビジネスロジックは Kotlin Multiplatform で共有し、UI は SwiftUI と Jetpack Compose でそれぞれ実装することもできる。

私たちは、新規にアプリを作る greenfield プロジェクトも、既存アプリを拡張・改修する brownfield プロジェクトも手がける。どちらも React Native、ネイティブ UI、あるいはその混成で作れる。どこから始めるかによって、アーキテクチャをどう導入し、どう段階的に育てていくかが決まる。

明確なアーキテクチャ、定義のしっかりしたタスク、信頼できる検証手段があれば、AI コーディングエージェントはこれら3つの方式の実装と最適化をはるかに速くする。これらの技術スタックをまたいだ経験があるからこそ、どの方式がどこで最も価値を発揮するかを見極められる。Gobi は、このバランスがプロダクトの成長とともに変わることを示す一例だ。

React Native からネイティブへのスペクトル:コード共有、ネイティブモジュール、完全ネイティブ

同じスペクトル上の3つの方式:React Native、ネイティブモジュールを伴う React Native、完全ネイティブ。

Gobi における React Native からネイティブへの段階的移行

Gobi は、インタラクティブな地図上で場所やイベントを見つけ、保存、評価できるアプリだ。おすすめは AI アシスタントが提示し、結果と詳細は地図の上に重なるドラッグ可能なパネルに表示される。そのため、ユーザーが探索している間も UI は常に同期していなければならない。

Gobi が Software Mansion に声をかけたとき、チームは React Native アプリを開発中で、中核となるインタラクションを磨き、応答性を高め、その後のプロダクト開発を支えるモバイル開発の専門性を必要としていた。

開発を進めるうちに、カメラの移動、マーカーの描画、パネルのドラッグ、スクロール、キーボード処理に問題が見つかった。ナビゲーションと検索はときに鈍く感じられ、フレームレートの低下によって、チームが Gobi に思い描いていた滑らかな120 FPS の体験には届いていなかった。総じて、洗練度と応答性はこちらの期待に達していなかった。まずは既存の React Native アプリ内で的を絞ったネイティブの改善を行い、これらの問題を1つずつ解消していった。

Gobi のボトムパネルを改善する過程で、私たちはReact Native Bottom Sheetを作った。パネルの移動とジェスチャーはネイティブコードが処理し、その上に React Native のコンテンツを載せる。これにより、ドラッグするパネルがそのコンテンツのスクロールへどう移行するかを細かく制御できるようになった。このライブラリはオープンソースなので、自分の React Native アプリでそのまま使える。

その後、独自のネイティブ地図モジュールを開発し、マーカー、カメラの動き、アプリ固有の最適化においてより大きな自由度を得た。地図データとインタラクションをネイティブコードで処理することで、地図上に表示する要素の数を減らすことなく、目標の120 FPS を達成した。

この時点で、私たちは React Native の UI の下に、増え続けるネイティブモジュール群を抱えていた。どのモジュールも独自のブリッジを伴う。アダプター、共有状態、そして両側で一致させなければならない挙動だ。しかも Gobi のロードマップにある要求の厳しい機能は、どれも同じ手順をもう一度踏む必要があった。検討した結果、このブリッジを保守し、以降の機能を2つの層にまたがって実装するコストは、既存コードを一度ネイティブへ移すコストを上回った。そこでアプリ全体を Swift と Kotlin へ移行することを決めた。

移行は段階的に進めた。当初、ネイティブのコンテンツは React が制御するシートの中に埋め込まれていた。その後、ネイティブコードがナビゲーションと外側のコンテナを引き継ぎ、既存の React Native コンポーネントは対応する代替実装ができるまでネイティブアプリ内に残した。私たちはこれらの埋め込みビューを React islands と呼んだ。機能と共有状態は徐々に移行し、各 island はネイティブ版が引き継いだ時点で外された。明確な計画と AI コーディングエージェントがこの移行を支え、その間も新機能のリリースは続いた。React に依存する機能がなくなったところでランタイムを削除し、ネイティブの iOS アプリと Android アプリへの移行を完了した。

すべてのアプリに移行が必要なわけではない

Gobi はスペクトル上の1点にすぎない。私たちが手がけるアプリの大半では、React Native が土台であり続け、ネイティブコードはプロダクトが本当に必要とする箇所にだけ使う。Filamentは招待制のチャットプラットフォームで、WebView ベースのエディタが要求に追いつかなくなったため、完全ネイティブのリッチテキスト入力を作った。これがReact Native Enrichedとしてオープンソースになっている。Android の通知処理もネイティブコードへ移し、暗号化されたメッセージが JavaScript ランタイム全体の起動を待たなくて済むようにした。アプリ自体は React Native のままで、iOS、Android、iPad のレイアウト、そして同じ Metro 設定でビルドする Web 版が1つのコードを共有している。メッセージの暗号化とシンタックスハイライトに絞った最適化により、Android のコールドスタートは6〜10秒から約800 ms に短縮され、アプリサイズは30%以上削減された。

正解がスペクトル上の別の位置にあることもある。SeaPeople がクラッシュとバッテリー消費に悩まされていたとき、原因は技術スタックではなかった。本当の原因はバックグラウンドタスクの扱い方と、毎秒再レンダリングされる UI にあった。React Native の内部でこれらを修正した結果、iPhone 13での CPU 使用率は170%から30%に下がり、クラッシュフリー率は95%から約99%に向上した。プロダクトの成長も支援した。2人のチームが2週間で、新しい Groups 機能の最初のバージョンを作り上げた。

Vibecodeは最初から別の道を選んだ。このプロダクトはユーザーが生成したアプリを動かすため、iOS アプリをネイティブの SwiftUI で構築し、React Native をそれらアプリの制御された実行環境として組み込んだ。ここでは、React Native が実行時に JavaScript bundle を読み込めること自体がプロダクトの特性だ。生成された各 Expo アプリは JavaScript bundle として公開され、ホストアプリが取得、キャッシュ、実行する。React Native の Element Inspector は、AI コーディングエージェントが編集している UI の構造化情報を提供する。仕組みは Gobi の移行における React islands によく似ているが、こちらは恒久的なアーキテクチャだ。

結論

選ぶ技術は、これから作るプロダクトに合っていなければならない。どの選択肢を採るかを決めるときは、プロダクトの中核機能、リリース目標、想定される成長、チームの状況から見ていく。

アプリの技術選定を進めるには:

  • React Native を選ぶ:UI とロジックをクロスプラットフォームで共有したいとき。ツールとライブラリのエコシステムを活かして開発・テスト・リリースを速め、プロダクトの成長に合わせてアプリを拡張できる柔軟性も確保したいときに適する。
  • ネイティブ連携を加える:一部の機能にカスタム UI、プラットフォーム API、狙いを定めたパフォーマンス最適化が必要なとき。レンダリング、アニメーション、負荷の高いデータ処理などが該当する。アプリの残りの部分は共有のままにできる。
  • 完全ネイティブを検討する:アプリの大部分でプラットフォームを直接制御する必要があるとき。重いデータ処理や、頻繁に連携が必要なインタラクションなどが該当する。既存プロダクトなら、同じ要件を的を絞った改善や移行で満たすのにどれだけの作業がかかるかと比較する。どちらの見積もりにもテストと継続的なメンテナンスを含める。
  • 移行が適したタイミングで段階的に進める:代替の準備が整うまで、既存の React Native ビューをネイティブアプリ内に残す。ユーザーは移行の途中に気づかず、チームはバグ修正と新機能のリリースを続けられる。私たちが Gobi で行ったのはこれだ。

Software Mansion での私たちの仕事も同じ考え方に沿っている。モバイルアプリとその土台となるライブラリを構築してきた経験をもとに、チームの技術選定を支援し、プロダクトの成長に合わせて技術移行を導く。

よくある質問

React Native でアプリを構築する利点は何ですか?

React Native を使えば iOS と Android 向けのネイティブアプリを構築でき、チームは UI とビジネスロジックをクロスプラットフォームで共有できる。各種ライブラリ、開発者ツール、Expo のサービスが開発を加速し、素早い反復を支える。ある機能にカスタムのネイティブコードが必要なら、Swift や Kotlin のモジュールやコンポーネントを必要な分だけ組み込める。共有コードベースの柔軟性を失うことはない。

React Native と Swift、Kotlin の比較:ネイティブが適するのはどんなとき?

カスタムのインタラクション、継続的なデータ処理、深いプラットフォーム統合が体験の中心にあるなら、ネイティブ開発が適する。レンダリング、並行処理、プラットフォーム API を直接制御できるため、負荷の高いワークフローを最適化できる。明確なアーキテクチャと統一された振る舞いの取り決めがあれば、AI コーディングエージェントが複数プラットフォームでの実装とテストを加速する。ビジネスロジックは Kotlin Multiplatform で共有しつつ、各プラットフォームの UI は SwiftUI と Jetpack Compose でそれぞれ実装することもできる。

React Native とネイティブコードは混在できますか?

できる。ネイティブモジュールはネイティブ API とロジックを、ネイティブコンポーネントはネイティブ UI を外部に公開する。使うインターフェースは Turbo Native Modules と Fabric Native Components だ。連携の範囲は、1 つの機能だけでも、ネイティブのデータ処理・ロジック・ UI を含む機能一式でもよい。逆も同じで、ネイティブアプリに React Native のビューやフローを React islands として埋め込める。Gobi は移行期間中にこれをやった。この柔軟性のおかげで、どの責務を共有のまま残し、どれをネイティブコードに任せるかを決められる。

Kotlin Multiplatform と React Native の比較:どちらを選ぶべき?

UI とロジックを 1 つのコードベースでまかない、非常に成熟したライブラリエコシステム、大きなコミュニティ、OTA アップデートを重視するなら React Native を選ぶ。共有 UI は iOS と Android のどちらでもネイティブのプラットフォームコンポーネントとして描画され、Fast Refresh が変更を即座に反映する。公開されている React Native のコードが膨大なため、AI コーディングエージェントもこれをよく理解している。

以上の問いがネイティブ開発を指し、それでもコードを共有したいなら Kotlin Multiplatform を選ぶ。共有した Kotlin コードは Android では JVM バイトコードに、iOS ではネイティブのマシンコードにコンパイルされる。共有ロジックはネイティブの速度で動き、アプリのサイズも小さくなる。Kotlin は厳格な型安全性を備え、コルーチンとディスパッチャによる真のマルチスレッド実行をサポートするため、CPU 負荷の高いタスクを並列に動かせる。ビジネスロジックだけを共有し、UI は SwiftUI と Jetpack Compose でそれぞれ書くこともできるし、Compose Multiplatform などで UI ごと共有することもできる。

両者は組み合わせても使える。React Native アプリからネイティブモジュール経由で Kotlin Multiplatform のロジックを呼び出せる。

既存のアプリでも支援してもらえますか?

はい。まずアプリの現状を把握する。ネイティブ(Swift、Kotlin)であれクロスプラットフォーム(React Native、Flutter、Kotlin Multiplatform)であれ同じだ。そのうえで次の一歩を一緒に決める。的を絞った改善、ネイティブ連携、あるいは Gobi のように段階的な移行。移行は新機能の開発を置き換えるのではなく、それと並行して進める。プロダクトを前に進め続けられる。アプリについて相談する。

アプリに成長の余地を残す

プロダクトにはそれぞれ固有の難しさがある。計画を Software Mansion に話せば、実行可能な道筋に変える手伝いができる。初回リリースを準備している段階でも、既存アプリをさらに前へ進めたい段階でもいい。

出典: Software Mansion Blog← ホームへ戻る