既存のネイティブiOS/Androidアプリを書き換えることなくExpoを追加する方法を学びます。Expo SDK 55の新しい分離型brownfieldワークフローについて説明します。
概要
本番環境で稼働している既存のネイティブアプリがあるとします。それは長年にわたるプラットフォーム固有の決定の結果であり、完全に置き換えたり一度にすべてを変更したりするものではありません。同時に、そのアプリ内で新機能をReact Nativeを使用して構築したい場合があります(例えば、プラットフォーム間でロジックを共有するため)が、それを可能にするためにアプリの残りの部分を書き換える必要はありません。
これがbrownfieldの意味するところです:メインエントリーポイントがReact Nativeではない既存のネイティブアプリにReact NativeとExpoを追加することです。問題はReact Nativeを追加できるかどうかではなく、全か無かのアーキテクチャ決定ではなく、段階的で自己完結型で、より広いチームに破壊的でない方法でそれを導入する方法です。
SDK 55での新機能
SDK 55では、非React Nativeネイティブアプリにexpoの画面とコンポーネントを統合できる分離型brownfieldワークフローを導入しました!これは、プリコンパイルされたExpoアプリをネイティブ依存関係として埋め込むことで実現され、新しいフレームワークを採用するというよりもライブラリを追加するような体験に近くなります。
既存ネイティブアプリでのExpoの位置づけ
チームが既存のネイティブアプリにReact Nativeを導入することを検討する理由はさまざまです:
- 段階的採用: コードベース全体を一度にコミットすることなく、アプリの一部でExpoの使用を開始し、時間をかけてその使用を拡大する可能性を残す
- 選択的使用: 自己完結型の機能エリアやサブアプリ(例:FacebookアプリのMarketplace)など、製品の特定の部分を構築するためにReact Nativeを選択的に使用し、アプリの残りの部分は変更しない
- ビジネスニーズへの対応: 買収後に既存のReact Nativeアプリをネイティブ製品に統合するなど、技術的なものではなくビジネスニーズに対応する
これらのシナリオ全体で要件は同じです:React Nativeは、痛みを伴う書き換えや既存アプリの不自然な歪曲なしに、すでに存在するアプリに適合する必要があります。
Expoのbrownfieldアプローチ
Expoのbrownfieldアプローチは、コードベースとそれに取り組むチームへの破壊を最小限に抑えてReact Nativeの使用を可能にすることを目的としています。Expoは、サポートされ保守可能な方法でネイティブアプリにReact Nativeを埋め込むための明示的なAPIとパターンを提供します。
Expoでのbrownfieldアプローチ
Expoは現在、既存のネイティブアプリにReact Nativeを追加する2つの異なる方法を文書化しています:
統合アプローチ
統合アプローチは、React NativeとExpoを直接ネイティブアプリにインストールします。しかし、React Nativeをネイティブアプリに埋め込むことは、チーム全体が対応しなければならない二次的なランタイム、ビルドシステム、開発環境を導入するため、典型的なライブラリを追加するよりも複雑です。
分離アプローチ(SDK 55の新機能)
代替として、SDK 55で分離アプローチを導入しました。これは、React Nativeコードをネイティブライブラリ(AndroidのAARまたはiOSのXCFramework)としてパッケージ化し、他の依存関係と同様にネイティブアプリに統合できます。
ネイティブ開発者は、Node.js環境をセットアップしたり、React Nativeのビルド依存関係を扱ったりする必要がありません。彼らは単にプリビルドされたアーティファクトを消費するだけで、複雑さはReact Nativeチーム内に含まれ、組織の残りの部分には見えません。
分離アプローチの実践
分離アプローチでは、Expoアプリは事前にビルドされ、ネイティブバイナリアーティファクトとして配布されます。Expoの観点から見ると、これは単なるExpoプロジェクトです。
セットアップと使用
分離アプローチは新しいexpo-brownfieldパッケージで実装されています:
npx expo install expo-brownfield
npx expo-brownfield build:ios
npx expo-brownfield build:android
iOS実装
iOSでは、プロジェクト内にartifactsディレクトリが作成され、ネイティブフレームワークとしてコンパイルされたアプリの出力が含まれます:
├── app/
├── artifacts/
│ ├── hermesvm.xcframework/
│ └── expohelloworldbrownfield.xcframework/
├── assets/
├── ios/
├── node_modules/
├── .gitignore
└── app.json
生成された.xcframeworkをXcodeプロジェクトにドラッグします。そこから、他の依存関係と並んで表示され、アプリにリンクされます。
Android実装
Androidでは、出力は.aarとしてパッケージ化されますが、配布方法が少し異なります。ファイルをネイティブプロジェクトにコピーするのではなく、ビルドはローカルMavenディレクトリ(通常は~/.m2)にアーティファクトを公開します。
// settings.gradleまたはbuild.gradle
dependencyResolutionManagement {
repositories {
mavenLocal()
google()
mavenCentral()
}
}
埋め込みアプリは、それをホストするモジュールで通常の依存関係として追加できます:
dependencies {
implementation("com.example.helloworld:brownfield:1.0.0")
}
テーマ設定
ホストアクティビティは、React Nativeが期待する属性を提供するテーマを使用する必要があります:
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
- android:theme="@style/Theme.Helloworld">
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
ネイティブと埋め込みアプリ間の通信
分離アプローチには、ネイティブホストと埋め込みExpoアプリ間でメッセージを渡すための組み込み通信APIが含まれています。このAPIは、埋め込みアプリの内部モジュールへの直接アクセスを必要とせずに、イベントとデータを交換するための構造化された双方向チャネルを提供します。
iOSでのメッセージ送信
import ExpoBrownfield
BrownfieldMessaging.sendMessage([
"type": "MyIOSMessage",
"timestamp": Date().timeIntervalSince1970,
"data": ["platform": "ios"]
])
React Nativeでのメッセージ受信
import * as Brownfield, { type MessageEvent } from 'expo-brownfield';
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
console.log('Received message:', event);
};
Brownfield.addMessageListener(handleMessage);
return () => {
Brownfield.removeMessageListener(handleMessage);
};
}, []);
}
制限事項とトレードオフ
分離アプローチの制限事項
-
単一埋め込みアプリ: ネイティブアプリには1つの埋め込みアプリのみを含めることができます。各埋め込みアプリには独自のReact Nativeランタイムのコピーが含まれているため、複数のフレームワークを含めるとビルド時にクラス名の衝突が発生します
-
共有ランタイム: 複数の論理的な体験は単一の埋め込みランタイムを共有する必要があります
-
自己完結性: 埋め込みアプリは意図的に自己完結型です。埋め込みアプリ外のコードは、Expoモジュールやその他の内部実装の詳細に直接アクセスできません
-
ビルド時のトレードオフ: フレームワークやAARのビルドは遅く、プリコンパイルされたReact Nativeバイナリはこのコンテキストでは再利用できません
ライブラリ互換性の考慮事項
一部のライブラリは、brownfieldセットアップで使用する場合、期待通りに動作しない場合や、限定的なドキュメントしかない場合があります。これには特定のExpoライブラリも含まれます。
例えば、expo-updatesパッケージは統合および分離brownfieldアプローチの両方で利用可能ですが、現在はネイティブアプリごとに単一のExpoプロジェクトと単一の更新URLを想定しています。