react-native-brownfield-playground
v1.1.2
Published
Brownfield React Native SDK (RN 0.86+, New Architecture) — present/dismiss UI and session APIs for React Native, Android, and iOS hosts
Maintainers
Readme
react-native-brownfield-playground
Embed React Native screens into existing React Native, Android, and iOS apps. Your native UI stays native — RN opens only when you call present.
| | |
|---|---|
| Install | npm install react-native-brownfield-playground |
| Setup | npx react-native-brownfield-playground init --android / --ios |
| Screens | "welcome" · "profile" |
| React Native | 0.86.2 (peers: react ^19.2.3, react-native ^0.86.0) |
| New Architecture | Required |
Public APIs
| Action | React Native | Android (Kotlin) | iOS (Swift) |
|--------|--------------|------------------|---------------|
| Present UI | EasySdk.present('welcome') | EasySdk.present(activity, "welcome") | EasySdk.shared.present(...) |
| Dismiss | EasySdk.dismiss() | EasySdk.dismiss() | EasySdk.shared.dismiss() |
| Get session | await EasySdk.getSession() | EasySdk.getSession(ctx) | EasySdk.shared.getSession() |
| Clear session | EasySdk.clearSession() | EasySdk.clearSession(ctx) | EasySdk.shared.clearSession() |
| Logged in? | EasySdk.isLoggedIn() | EasySdk.isLoggedIn(ctx) | EasySdk.shared.isLoggedIn() |
Docs live on the npm package page (this README). After install you can also open:
node_modules/react-native-brownfield-playground/README.md
Contents
- Prerequisites
- Install
- React Native apps
- Native Android apps
- Native iOS apps
- Verify setup
- Breaking changes in 1.1.0
- Troubleshooting
What’s new in 1.1.2
- Docs are npm-only (no private GitHub links); full guide lives in this README
- SwiftUI host snippets:
AppDelegate.SwiftUI.snippet,ContentView.SwiftUI.snippet - iOS troubleshooting: User Script Sandboxing / embed frameworks fix
- Podspec sources the public npm tarball (not a private git repo)
What’s new in 1.1.1
init --androidauto-fixes Android Studio gotchas:PREFER_SETTINGS,coreKtx = 1.16.0, rootforce(androidx.core), Java 17- New snippets:
android-root-build.gradle*,android-libs.versions.toml.snippet - Docs/checklist for MavenRepo + AAR metadata (
core:1.19.0) failures
Prerequisites
| Tool | Version |
|------|---------|
| Node.js | 20+ |
| React | 19.2.3 |
| React Native | 0.86.2 |
| JDK | 17 (Android) |
| Android minSdk | 24+ |
| Xcode | 16+ recommended |
| iOS deployment target | 15.1+ |
| New Architecture | ON |
Install
From your app root (the folder that contains android/ and/or ios/):
npm init -y # if needed
npm install [email protected] [email protected] react-native-brownfield-playgroundAutolinking picks up android/ and EasySdk.podspec from the package. No manual linking.
React Native apps
npm install react-native-brownfield-playground [email protected] [email protected]
npx pod-install # iOS onlyWrap the app once with <EasySdkProvider>, then call EasySdk.present(...):
import React from 'react';
import {Button, View} from 'react-native';
import {EasySdk, EasySdkProvider} from 'react-native-brownfield-playground';
export default function App() {
return (
<EasySdkProvider>
<View style={{flex: 1, justifyContent: 'center', gap: 12, padding: 24}}>
<Button
title="Open Welcome"
onPress={() =>
EasySdk.present('welcome', {
props: {userId: 'rn_user_001'},
onResult: result => console.log(result),
})
}
/>
<Button title="Open Profile" onPress={() => EasySdk.present('profile')} />
<Button
title="Log session"
onPress={async () => console.log(await EasySdk.getSession())}
/>
<Button title="Clear session" onPress={() => EasySdk.clearSession()} />
</View>
</EasySdkProvider>
);
}Native Android apps
Keep your existing Android UI. React Native screens open only via EasySdk.present.
Expected layout
YourNativeApp/ ← run all npm commands here
├── android/ ← your existing Android app
├── package.json
├── index.js ← Metro entry (registers EasySdkRoot)
├── metro.config.js
├── babel.config.js
├── react-native.config.js
└── node_modules/Step 1 — Install + init
cd /path/to/YourNativeApp
npm init -y
npm install [email protected] [email protected] react-native-brownfield-playground
npx react-native-brownfield-playground init --android
npm install # if init added dependencies
npx react-native-brownfield-playground doctorinit will (when files are found):
- Create JS host files (
index.js, Metro/Babel configs) if missing - Enable Hermes + New Architecture in
gradle.properties - Fix
repositoriesMode→PREFER_SETTINGS(avoids MavenRepo / RN plugin failure) - Patch
settings.gradle/app/build.gradlefor RN autolinking - Force
androidx.core1.16.0 at root + pincoreKtxin Version Catalogs - Set Java / Kotlin JVM target to 17
- Write
EasyHostApplication.ktwhen no Application class is set, and wire it in the manifest - Copy reference snippets to
templates-generated/
Backups of patched files are saved as *.bak.
Android Studio checklist (verify after init)
| Item | Must be |
|------|---------|
| repositoriesMode | PREFER_SETTINGS |
| coreKtx in libs.versions.toml | 1.16.0 (not 1.19.0) |
| Root Gradle force(androidx.core…) | present |
| compileSdk | 36 |
| AGP | 8.x (do not upgrade to 9.1 for core 1.19) |
| Java / jvmTarget | 17 |
| newArchEnabled / hermesEnabled | true |
Snippets: templates/android-*.snippet (also copied to templates-generated/).
Step 2 — Application must expose reactHost
Your Application must implement ReactApplication and provide a New Architecture ReactHost:
class EasyHostApplication : Application(), ReactApplication {
override val reactHost: ReactHost by lazy {
getDefaultReactHost(
context = applicationContext,
packageList = PackageList(this).packages,
jsMainModulePath = "index",
)
}
override fun onCreate() {
super.onCreate()
loadReactNative(this)
}
}Point the manifest at it:
<application android:name=".EasyHostApplication" ...>If you already have an Application class, merge the reactHost + loadReactNative pieces into it (see templates/EasyHostApplication.kt or templates-generated/ after init).
Step 3 — Call from your Activity
import com.demo.easysdk.EasySdk
EasySdk.present(this, "welcome", mapOf("userId" to "u1")) { status, payload ->
val session = EasySdk.getSession(this)
}
EasySdk.present(this, "profile")
EasySdk.getSession(this)
EasySdk.clearSession(this)
EasySdk.isLoggedIn(this)Full sample: examples/android-MainActivity.kt.
Step 4 — Run (debug)
# Terminal 1 — Metro must be running
npm start
# Terminal 2
adb reverse tcp:8081 tcp:8081 # physical device / some emulators
cd android && ./gradlew installDebugRelease (no Metro)
npx react-native bundle --platform android --dev false --entry-file index.js \
--bundle-output android/app/src/main/assets/index.android.bundle \
--assets-dest android/app/src/main/res
cd android && ./gradlew assembleReleaseNative iOS apps
Keep your existing native UI (UIKit or SwiftUI). React Native screens open only via EasySdk.shared.present.
Expected layout
YourNativeApp/
├── ios/ ← your existing Xcode project
├── package.json
├── index.js
├── metro.config.js
├── babel.config.js
├── react-native.config.js
└── node_modules/Step 1 — Install + init
cd /path/to/YourNativeApp
npm init -y
npm install [email protected] [email protected] react-native-brownfield-playground
npx react-native-brownfield-playground init --ios
npm install
cd ios && pod install
cd ..
npx react-native-brownfield-playground doctorAlways open ios/*.xcworkspace, not the .xcodeproj.
Step 2 — Bind EasySdk (pick your app style)
A) SwiftUI host (Xcode “App” template — @main on *App.swift)
This is the common new Xcode template. Do not paste the UIKit @main AppDelegate as-is (two @main entries will fail).
- Use
templates/AppDelegate.SwiftUI.snippet(no@main, noUIWindow). - Attach it from your App:
@main
struct HostIOSApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup { ContentView() }
}
}- Call
presentfrom SwiftUI by resolving aUIViewController(seetemplates/ContentView.SwiftUI.snippet):
EasySdk.shared.present(from: topViewController(), screen: "welcome", props: ["userId": "ios_user"]) { _, _ in }B) UIKit host (AppDelegate is @main)
Use templates/AppDelegate.swift.snippet: create RCTReactNativeFactory, call EasySdk.shared.bind(factory:), keep a native root VC.
EasySdk.shared.bind(factory: factory) // once in AppDelegate
EasySdk.shared.present(from: self, screen: "welcome", props: ["userId": "ios_user"]) { status, payload in }
EasySdk.shared.present(from: self, screen: "profile")ObjC alternative: templates/AppDelegate.mm.snippet.
Breaking vs 1.0.x: do not pass RCTBridge into present.
Step 3 — Run (debug)
# Terminal 1
npm start
# Terminal 2
open ios/*.xcworkspace
# Product → Run (Simulator is easiest for Metro/localhost)Release (no Metro)
npx react-native bundle --platform ios --dev false --entry-file index.js \
--bundle-output ios/main.jsbundle --assets-dest iosEnsure release bundleURL points at that file (see AppDelegate snippet).
Verify setup
npx react-native-brownfield-playground doctorBreaking changes in 1.1.0
| Old (≤1.0.x) | New (1.1.0) |
|--------------|-------------|
| ReactInstanceManager / legacy root view | ReactHost + ReactDelegate |
| iOS present(..., bridge: RCTBridge, ...) | EasySdk.shared.bind(factory:) then present(...) |
| RN 0.76 / React 18 | RN 0.86.2 / React 19.2.3 |
| Optional New Arch | New Arch required |
Troubleshooting
| Problem | Fix |
|---------|-----|
| Red screen / Metro HTTP 500 | Start Metro from the app root; ensure index.js has require('react-native-brownfield-playground') |
| reactHost is null | Implement ReactApplication.reactHost with getDefaultReactHost |
| EasySdk.bind / present crash on iOS | Call EasySdk.shared.bind(factory:) in AppDelegate before any present |
| Blank RN screen | Metro not reachable — npm start + adb reverse tcp:8081 tcp:8081 |
| MavenRepo was added by plugin 'com.facebook.react' | In android/settings.gradle(.kts) change FAIL_ON_PROJECT_REPOS → PREFER_SETTINGS |
| AAR metadata: androidx.core:core:1.19.0 needs compileSdk 37 / AGP 9.1 | Do not jump to AGP 9. Set coreKtx = "1.16.0" in android/gradle/libs.versions.toml, and force("androidx.core:core:1.16.0") in the root build.gradle(.kts). Soft constraints alone lose to a Version Catalog pin. Re-run init --android. |
| Java 11 compile / desugar issues | Set Java / Kotlin jvmTarget to 17 |
| Autolink / Gradle errors | Re-run init --android, compare with templates/android-*.snippet |
| Pods missing EasySdk | cd ios && pod install, open .xcworkspace |
| iOS [CP] Embed Pods Frameworks / rsync Operation not permitted | In Xcode → Build Settings set User Script Sandboxing (ENABLE_USER_SCRIPT_SANDBOXING) to No, then Clean Build Folder |
| Two @main / missing ViewController on SwiftUI app | Use AppDelegate.SwiftUI.snippet + @UIApplicationDelegateAdaptor (do not use UIKit @main AppDelegate as-is) |
| Old RCTBridge samples | Those are legacy — use ≥1.1.2 New Arch templates |
License
MIT
