ClaudeExpo2026/03/05 14:30

Expo brownfield: How to add Expo to your existing native app without a rewrite

要点だけを先に読めるように短く再構成したセクションです。

元記事

Quick Digest

要約

要点だけを先に読めるように短く再構成したセクションです。

claudejamodel: claude-sonnet-4-20250514

Expo SDK 55の新機能:既存ネイティブアプリにExpoを統合する分離型ブラウンフィールドアプローチ

Key Points

  • 既存ネイティブアプリへの非破壊的なExpo統合が可能
  • プリコンパイルされたバイナリアーティファクトとして配布
  • ネイティブ開発者のNode.js環境設定が不要

Summary

Expo SDK 55では、既存のネイティブアプリを書き換えることなくReact NativeとExpoを追加できる新しい分離型ブラウンフィールドワークフローが導入されました。この手法では、ExpoアプリをネイティブライブラリとしてプリコンパイルしてiOS用XCFrameworkやAndroid用AARとして配布することで、フレームワーク導入というよりもライブラリ追加に近い体験を実現します。

Key Points

  • 分離型アプローチの特徴

    • expo-brownfieldパッケージを使用してiOS XCFrameworkとAndroid AARを生成
    • ネイティブ開発者はNode.js環境の設定が不要
    • Continuous Native Generation (CNG)を活用した自動プロジェクト生成
  • 実装プロセス

    • ExpoプロジェクトとしてReact Nativeコードを開発
    • npx expo-brownfield build:ios/androidでバイナリアーティファクトを生成
    • 生成されたフレームワークを既存ネイティブアプリに依存関係として追加
  • 通信API

    • ネイティブアプリとEmbedded Expoアプリ間でのメッセージ交換をサポート
    • WebのpostMessageモデルに類似した双方向通信
    • 構造化されたイベント・データ交換メカニズム
  • 制限事項

    • 1つのネイティブアプリにつき1つのEmbedded Expoアプリのみサポート
    • ビルド時間が長く、デバッグ/リリース切り替え時にアーティファクト再生成が必要
    • 一部のライブラリ(expo-updatesなど)で制限あり

Full Translation

翻訳

原文の流れを保ったまま読める翻訳セクションです。

claudejamodel: claude-sonnet-4-20250514

Expo brownfield: 既存のネイティブアプリを書き換えることなくExpoを追加する方法

既存のネイティブ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

# iOS XCFrameworkをビルド
npx expo-brownfield build:ios

# Android AARをビルド
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でのメッセージ送信

// 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を想定しています。

Expo ブラウンフィールド:既存のネイティブアプリにリライトせずに Expo を追加する方法 | Expo | DocsDigest