Visão Geral do expo-brownfield
Guia para enviar módulos Expo dentro de shells iOS/Android existentes - desde a instalação do expo-brownfield até a publicação de artefatos e integração com o aplicativo host.
Busque em todas as páginas da documentação
Guia para enviar módulos Expo dentro de shells iOS/Android existentes - desde a instalação do expo-brownfield até a publicação de artefatos e integração com o aplicativo host.
Cartão de receita de referência rápida - pronto para copiar e colar.
# 1. Crie o módulo RN (repositório separado ou pacote monorepo)
npx create-expo-app@latest CheckoutModule --template default@sdk-57
cd CheckoutModule
npx expo install expo-brownfield// app.config.ts - plugin + módulos pré-compilados opcionais do iOS
{
"expo": {
"scheme": "checkout",
"plugins": [
["expo-build-properties", { "ios": { "usePrecompiledModules": true } }],
[
"expo-brownfield",
{
"ios": { "targetName": "CheckoutBrownfield" },
"android": {
"group": "com.example",
"libraryName": "checkout-brownfield",
"package": "com.example.checkout.brownfield",
"version": "1.0.0"
}
}
]
]
}
}# 2. Gere os targets nativos do brownfield (código nativo de depuração, codegen de CI)
npx expo prebuild
# 3. Construa os artefatos
npx expo-brownfield build:android --release
npx expo-brownfield build:ios --release --package CheckoutPackage
# 4. Loop de desenvolvimento - Metro + build de depuração do host
npx expo start
# Inicie o aplicativo host do Xcode / Android Studio → a tela RN carrega do Metro// Host Android - dependência build.gradle.kts (versão da configuração do plugin)
dependencies {
implementation("com.example:checkout-brownfield:1.0.0")
}// Host iOS - AppDelegate + apresente RN
import CheckoutBrownfield
ReactNativeHostManager.shared.initialize()
let vc = ReactNativeViewController(moduleName: "main", initialProps: ["cartId": id])
navigationController?.pushViewController(vc, animated: true)Quando usar isso:
BrownfieldMessaging) entre o host e o RN sem escrever um TurboModule personalizado no primeiro dia.O projeto RN não precisa residir dentro do repositório nativo. Um monorepo packages/checkout-rn/ ou um repositório Git separado funcionam.
npx create-expo-app@latest CheckoutModule --template default@sdk-57
cd CheckoutModule
npx expo install expo-brownfieldConfirme o pin do SDK:
{
"dependencies": {
"expo": "~57.0.4",
"expo-brownfield": "~57.0.3",
"react": "19.2.3",
"react-native": "0.86.0"
}
}Estruture as telas com rotas finas e recursos robustos por Noções Básicas de Arquitetura Mobile.
expo-brownfield adiciona um plugin de configuração que gera:
BrownfieldActivity, ReactNativeFragment, ReactNativeHostManager, BrownfieldMessagingReactNativeViewController, ReactNativeView (SwiftUI), ReactNativeHostManager, BrownfieldMessaging// app.config.ts
import type { ExpoConfig } from "expo/config";
const config: ExpoConfig = {
name: "Checkout Module",
slug: "checkout-module",
scheme: "checkout",
plugins: [
[
"expo-brownfield",
{
ios: {
targetName: "CheckoutBrownfield",
bundleIdentifier: "com.example.checkout.brownfield",
},
android: {
group: "com.example",
libraryName: "checkout-brownfield",
package: "com.example.checkout.brownfield",
version: "1.0.0",
publishing: [{ type: "localMaven" }],
},
},
],
],
};
export default config;Execute o prebuild para materializar os targets:
npx expo prebuildVeja Plugins de Configuração para padrões de plugin idempotentes ao combinar expo-brownfield com outros modificadores nativos.
npx expo-brownfield build:android --releaseO target de publicação padrão é o Maven local (~/.m2). O build.gradle.kts do host precisa:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
mavenLocal()
}
}
dependencies {
implementation("com.example:checkout-brownfield:1.0.0")
}Apresente o RN do host:
import com.example.checkout.brownfield.BrownfieldActivity
import com.example.checkout.brownfield.showReactNativeFragment
class CheckoutActivity : BrownfieldActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
showReactNativeFragment()
}
}A entrada AndroidManifest.xml precisa de um tema NoActionBar e configChanges para teclado/orientação - a documentação gerada de BrownfieldActivity lista o conjunto completo de atributos.
# XCFrameworks em ./artifacts/
npx expo-brownfield build:ios --release
# Swift Package autocontido (recomendado para integração Xcode do host)
npx expo-brownfield build:ios --release --package CheckoutPackageCaminho do Swift Package: No Xcode → Add Package Dependencies → Add Local → selecione artifacts/CheckoutPackage-release/. O Xcode vincula arquivos .xcframework empacotados (brownfield, Hermes, React, dependências).
Caminho do XCFramework: Arraste {TargetName}.xcframework e hermesvm.xcframework para o projeto host; defina Embed & Sign.
Inicialize cedo:
import CheckoutBrownfield
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
ReactNativeHostManager.shared.initialize()
return true
}
}Apresentação SwiftUI:
import SwiftUI
import CheckoutBrownfield
struct CheckoutButton: View {
@State private var showCheckout = false
var body: some View {
Button("Checkout") { showCheckout = true }
.fullScreenCover(isPresented: $showCheckout) {
ReactNativeView(moduleName: "main", initialProps: ["cartId": cartId])
}
}
}Construa pacotes de depuração e lançamento separadamente - os targets binários do SPM são específicos da versão.
// Módulo RN - envie eventos para o nativo
import * as Brownfield from "expo-brownfield";
export function completeCheckout(orderId: string) {
Brownfield.sendMessage({ type: "CHECKOUT_COMPLETE", orderId });
Brownfield.popToNative(true);
}// Android - receba do RN
BrownfieldMessaging.addListener { event ->
if (event["type"] == "CHECKOUT_COMPLETE") {
// Atualize a lista de pedidos nativa, remova o fragmento RN
}
}Habilite o botão de voltar do hardware para retornar ao nativo quando apropriado:
Brownfield.setNativeBackEnabled(true);Padrões completos de sessão: Autenticação Compartilhada e Pontes.
Quando dois módulos expo brownfield coexistem em um aplicativo iOS:
{
"plugins": [
[
"expo-brownfield",
{
"ios": {
"targetName": "CheckoutBrownfield",
"multipleFrameworks": true
}
}
]
]
}Isso prefixa os símbolos ObjC para evitar erros duplicados do linker. Trate como avançado - prefira um módulo RN com várias rotas até que os limites forcem uma divisão.
acme-mobile/
├── apps/
│ ├── host-ios/ # Xcode - consome Swift Package
│ └── host-android/ # Gradle - consome Maven AAR
├── packages/
│ └── checkout-rn/ # Projeto Expo com expo-brownfield
│ ├── app/
│ ├── app.config.ts
│ └── package.json
└── package.json # raiz do workspaceEsboço de CI:
# packages/checkout-rn - publique artefatos na tag
- run: npx expo-brownfield build:android --release
- run: npx expo-brownfield build:ios --release --package CheckoutPackage
- run: aws s3 sync ./artifacts s3://ci-artifacts/checkout-rn/${{ github.sha }}/Os pipelines do host baixam versões de artefatos fixadas - sem npm install em runners nativos. Detalhes: CI/CD Brownfield.
| Ferramenta Expo | Host isolado Brownfield |
|---|---|
| Módulos Expo SDK | ✓ |
| Expo Router | ✓ (dentro do módulo RN) |
expo start / Metro | ✓ (depuração) |
| EAS Build (projeto RN) | ✓ |
| EAS Update | ✓ (com disciplina de versão de runtime) |
| Expo Go | ✗ |
| Expo Dev Client no host | ✗ (use artefatos de depuração + Metro) |
O suporte Brownfield é alfa - fixe expo-brownfield na mesma linha de SDK que expo e leia as notas de lançamento a cada atualização.
Isolado quando a CI nativa deve permanecer sem Node. Integrado quando uma equipe edita nativo e RN em um único repositório diariamente. Veja Noções Básicas de Brownfield.
Targets de biblioteca Brownfield ao lado (não substituindo) os targets do aplicativo - use-os para depurar a cola nativa e executar comandos build:* CLI.
Aumente a versão de android.version / tag do pacote iOS, execute build:android / build:ios, publique artefatos, atualize as coordenadas de dependência do host - o mesmo que qualquer biblioteca nativa.
Sim - as rotas vivem inteiramente dentro do pacote RN. O nativo abre moduleName: "main"; o Router lida com caminhos internos.
useSharedState e mensagensexpo-brownfieldVersões da Stack: Esta página foi escrita para React 19.2.3, React Native 0.86.0 e Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026