diff --git a/README.md b/README.md
index f9517d16..018267b9 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,13 @@
- Compatible with all native languages **Objective-C**, **Swift**, **Java** and **Kotlin**
- Supports UIKit and SwiftUI on iOS and Fragments and Jetpack Compose on Android
+## React Native version compatibility matrix
+
+| Tested React Native Version | React Native Brownfield Version |
+| --------------------------- | ------------------------------- |
+| 0.81.x, 0.82.x | ^2.0.0-rc.0 |
+| 0.78.x | ^1.2.0 |
+
## Installation
The React Native Brownfield library is intended to be installed in a React Native app that is later consumed as a framework artifact by your native iOS or Android app.
@@ -51,16 +58,16 @@ npm install @callstack/react-native-brownfield
First, we need to package our React Native app as an XCFramework or Fat-AAR.
-#### With RNEF
+#### With Rock
-Follow [Integrating with Native Apps](https://www.rnef.dev/docs/brownfield/intro) steps in RNEF docs and run:
+Follow [Integrating with Native Apps](https://www.rockjs.dev/docs/brownfield/intro) steps in Rock docs and run:
-- `rnef package:ios` for iOS
-- `rnef package:aar` for Android
+- `rock package:ios` for iOS
+- `rock package:aar` for Android
#### With custom scripts
-Instead of using RNEF, you can create your own custom packaging scripts. Here are base versions for iOS and Android that you'll need to adjust for your project-specific setup:
+Instead of using Rock, you can create your own custom packaging scripts. Here are base versions for iOS and Android that you'll need to adjust for your project-specific setup:
- [Example iOS script](https://github.com/callstackincubator/modern-brownfield-ref/blob/main/scripts/build-xcframework.sh)
- [Example Android script](https://github.com/callstackincubator/modern-brownfield-ref/blob/main/scripts/build-aar.sh)
@@ -121,17 +128,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
-import com.callstack.rnbrownfield.RNViewFactory // exposed by RN app framework
class RNAppFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
- ): View? =
- this.context?.let {
- RNViewFactory.createFrameLayout(it)
- }
+ ): View? = ReactNativeBrownfield.shared.createView(activity, "BrownFieldTest")
}
```
@@ -166,7 +169,9 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- ReactNativeHostManager.shared.initialize(this.application)
+ ReactNativeHostManager.shared.initialize(this.application) {
+ println("JS bundle loaded")
+ }
showRNAppBtn = findViewById(R.id.show_rn_app_btn)
showRNAppBtn.setOnClickListener {
@@ -228,7 +233,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
| [
Michał Chudziak](https://twitter.com/michalchudziak)
[💻](https://github.com/callstack/react-native-brownfield/commits?author=michalchudziak "Code") [📖](https://github.com/callstack/react-native-brownfield/commits?author=michalchudziak "Documentation") [🤔](#ideas-michalchudziak "Ideas, Planning, & Feedback") | [
Piotr Drapich](https://twitter.com/dratwas)
[💻](https://github.com/callstack/react-native-brownfield/commits?author=dratwas "Code") [🤔](#ideas-dratwas "Ideas, Planning, & Feedback") |
-| :---: | :---: |
+| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
diff --git a/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeBrownfield.kt b/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeBrownfield.kt
index 5b08f883..7f599393 100644
--- a/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeBrownfield.kt
+++ b/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeBrownfield.kt
@@ -12,6 +12,7 @@ import com.facebook.react.ReactHost
import com.facebook.react.ReactInstanceEventListener
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.ReactContext
+import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
@@ -75,6 +76,9 @@ class ReactNativeBrownfield private constructor(val reactHost: ReactHost) {
context = application,
packageList = (options["packages"] as? List<*> ?: emptyList())
.filterIsInstance(),
+ jsMainModulePath = options["mainModuleName"] as? String ?: "index",
+ useDevSupport = options["useDeveloperSupport"] as? Boolean
+ ?: ReactBuildConfig.DEBUG,
jsRuntimeFactory = null
)
}
diff --git a/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeFragment.kt b/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeFragment.kt
index 0942da92..5efe9fd9 100644
--- a/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeFragment.kt
+++ b/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeFragment.kt
@@ -5,7 +5,7 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import com.callstack.reactnativebrownfield.constants.ReactNativeInitialPropsNames
+import com.callstack.reactnativebrownfield.constants.ReactNativeFragmentArgNames
import com.facebook.react.ReactFragment
import com.facebook.react.ReactHost
import com.facebook.react.bridge.Callback
@@ -36,13 +36,13 @@ class ReactNativeFragment : ReactFragment(), PermissionAwareActivity {
)
}
- moduleName = arguments?.getString(ReactNativeInitialPropsNames.ARG_MODULE_NAME)!!
+ moduleName = arguments?.getString(ReactNativeFragmentArgNames.ARG_MODULE_NAME)!!
this.reactDelegate =
ReactDelegateWrapper(
activity,
this.reactHost,
moduleName,
- arguments?.getBundle(ReactNativeInitialPropsNames.ARG_LAUNCH_OPTIONS)
+ arguments?.getBundle(ReactNativeFragmentArgNames.ARG_LAUNCH_OPTIONS)
)
}
@@ -113,9 +113,9 @@ class ReactNativeFragment : ReactFragment(), PermissionAwareActivity {
): ReactNativeFragment {
val fragment = ReactNativeFragment()
val args = Bundle()
- args.putString(ReactNativeInitialPropsNames.ARG_MODULE_NAME, moduleName)
+ args.putString(ReactNativeFragmentArgNames.ARG_MODULE_NAME, moduleName)
if (initialProps != null) {
- args.putBundle(ReactNativeInitialPropsNames.ARG_LAUNCH_OPTIONS, initialProps)
+ args.putBundle(ReactNativeFragmentArgNames.ARG_LAUNCH_OPTIONS, initialProps)
}
fragment.arguments = args
return fragment
diff --git a/android/src/main/java/com/callstack/reactnativebrownfield/constants/ReactNativeInitialPropsNames.kt b/android/src/main/java/com/callstack/reactnativebrownfield/constants/ReactNativeFragmentArgNames.kt
similarity index 89%
rename from android/src/main/java/com/callstack/reactnativebrownfield/constants/ReactNativeInitialPropsNames.kt
rename to android/src/main/java/com/callstack/reactnativebrownfield/constants/ReactNativeFragmentArgNames.kt
index e5c36a13..7a63bcc9 100644
--- a/android/src/main/java/com/callstack/reactnativebrownfield/constants/ReactNativeInitialPropsNames.kt
+++ b/android/src/main/java/com/callstack/reactnativebrownfield/constants/ReactNativeFragmentArgNames.kt
@@ -5,7 +5,7 @@ import com.facebook.react.ReactFragment
/**
* Convenience export of arguments that can be used
*/
-class ReactNativeInitialPropsNames private constructor() :
+class ReactNativeFragmentArgNames private constructor() :
ReactFragment() // subclass to gain access to protected constants
{
companion object {
diff --git a/docs/JAVA.md b/docs/JAVA.md
index 67367522..d0443893 100644
--- a/docs/JAVA.md
+++ b/docs/JAVA.md
@@ -25,13 +25,13 @@ With react-native >= 0.80.0, an auto-generated file was added which is responsib
you will have this file `ReactNativeApplicationEntryPoint` available. If you're consuming this library in a RN android library which is backed by
`com.callstack.react:brownfield-gradle-plugin`, then this file will also be available.
-Below is the code you need to add before you call `RNBrownfield.initialize`:
+Below is the code you need to add before you call `ReactNativeBrownfield.initialize`:
```java
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
loadReactNative(application);
-RNBrownfield.initialize(application, packages);
+ReactNativeBrownfield.initialize(application, packages);
```
@@ -62,15 +62,15 @@ A function used to initialize a React Native Brownfield singleton. Keep in mind
Params:
-| Param | Required | Type | Description |
-| ----------------------- | -------- | -------------------- | --------------------------------------------------------- |
-| application | Yes | `Application` | Main application. |
-| rnHost | No* | `ReactNativeHost` | An instance of [ReactNativeHost](https://bit.ly/2ZnwgnA). |
-| packages | No* | `List` | List of your React Native Native modules. |
-| options | No* | `HashMap` | Map of initial options. __Options listed below.__ |
-| onJSBundleLoaded | No* | `OnJSBundleLoaded` | Callback invoked after JS bundle is fully loaded. |
+| Param | Required | Type | Description |
+| ---------------- | ------------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| application | Yes | `Application` | Main application. |
+| reactHost | Exclusively* | `ReactHost` | An instance of [ReactHost](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt). |
+| packages | Exclusively* | `List` | List of your React Native Native modules. |
+| options | Exclusively* | `HashMap` | Map of initial options. __Options listed below.__ |
+| onJSBundleLoaded | Exclusively* | `OnJSBundleLoaded` | Callback invoked after JS bundle is fully loaded. |
-> * - Those fields aren't itself required, but at least one of them is. See examples below.
+> * - From the marked fields, exactly one must be specified, excluding the others. See examples below.
Available options:
- `useDeveloperSupport`: `Boolean` - Flag to use dev support.
@@ -103,7 +103,7 @@ private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
ReactNativeBrownfield.initialize(this, mReactNativeHost);
-OR
+// OR
ReactNativeBrownfield.initialize(this, mReactNativeHost, initialized -> {
// JS bundle loaded
@@ -115,7 +115,7 @@ List packages = new PackageList(this).getPackages();
ReactNativeBrownfield.initialize(this, packages);
-OR
+// OR
ReactNativeBrownfield.initialize(this, packages, initialized -> {
// JS bundle loaded
@@ -130,7 +130,7 @@ options.put("mainModuleName", "example/index");
ReactNativeBrownfield.initialize(this, options);
-OR
+// OR
ReactNativeBrownfield.initialize(this, options, initialized -> {
// JS bundle loaded
@@ -153,9 +153,9 @@ ReactNativeBrownfield.getShared()
**Properties:**
-| Property | Type | Default | Description |
-| --------------- | --------------- | -------------- | --------------------------------------------------------- |
-| reactNativeHost | `ReactNativeHost` | null | An instance of [ReactNativeHost](https://bit.ly/2ZnwgnA). |
+| Property | Type | Default | Description |
+| --------- | ----------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| reactHost | `ReactHost` | null | An instance of [ReactHost](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt). |
---
@@ -163,16 +163,16 @@ ReactNativeBrownfield.getShared()
`createView`
-Creates a React Native view with a given module name. It automatically uses an instance of React Native created in `startReactNative` method. This is useful when embedding React Native views directly in your native layouts.
+Creates a React Native view with a given module name. It automatically uses an instance of React Native created in `initialize` method. This is useful when embedding React Native views directly in your native layouts.
Params:
-| Param | Required | Type | Description |
-| -------------- | -------- | ------------------- | ----------------------------------------------------------- |
-| context | Yes | `Context` | Android context to create the view |
-| activity | No | `FragmentActivity` | Activity hosting the view, used for lifecycle management |
-| moduleName | Yes | `String` | Name of React Native component registered to `AppRegistry` |
-| launchOptions | No | `Bundle` | Initial properties to be passed to React Native component |
+| Param | Required | Type | Description |
+| ------------- | -------- | ------------------ | ---------------------------------------------------------- |
+| context | Yes | `Context` | Android context to create the view |
+| activity | No | `FragmentActivity` | Activity hosting the view, used for lifecycle management |
+| moduleName | Yes | `String` | Name of React Native component registered to `AppRegistry` |
+| launchOptions | No | `Bundle` | Initial properties to be passed to React Native component |
Returns:
`FrameLayout` - A view containing the React Native component.
@@ -193,7 +193,7 @@ container.addView(reactView);
#### `ReactNativeFragment`
-An fragment rendering `ReactRootView` with a given module name. It automatically uses an instance of a React Native created in `startReactNative` method. It works well with exposed JavaScript module. All the lifecycles are proxied to `ReactInstanceManager`. It's the simplest way to embed React Native into your navigation stack.
+An fragment rendering `ReactRootView` with a given module name. It automatically uses an instance of a React Native created in `initialize` method. It works well with exposed JavaScript module. All the lifecycles are proxied to `ReactInstanceManager`. It's the simplest way to embed React Native into your navigation stack.
```java
import com.callstack.reactnativebrownfield.ReactNativeFragment;
@@ -209,10 +209,10 @@ Creates a Fragment with `ReactNativeActivity`, you can use it as a parameter in
Params:
-| Param | Required | Type | Description |
-| ----------------------- | -------- | ------------------------------------------- | ----------------------------------------------------------- |
-| moduleName | Yes | `String` | Name of React Native component registered to `AppRegistry`. |
-| initialProps | No | `Bundle` \|\| `HashMap` \|\| `ReadableMap` | Initial properties to be passed to React Native component. |
+| Param | Required | Type | Description |
+| ------------ | -------- | ----------------------------------------------------- | ----------------------------------------------------------- |
+| moduleName | Yes | `String` | Name of React Native component registered to `AppRegistry`. |
+| initialProps | No | `Bundle` \|\| `HashMap` \|\| `ReadableMap` | Initial properties to be passed to React Native component. |
Examples:
diff --git a/docs/KOTLIN.md b/docs/KOTLIN.md
index d30eec9e..ca6e8dde 100644
--- a/docs/KOTLIN.md
+++ b/docs/KOTLIN.md
@@ -8,13 +8,13 @@ With react-native >= 0.80.0, an auto-generated file was added which is responsib
you will have this file `ReactNativeApplicationEntryPoint` available. If you're consuming this library in a RN android library which is backed by
`com.callstack.react:brownfield-gradle-plugin`, then this file will also be available.
-Below is the code you need to add before you call `RNBrownfield.initialize`:
+Below is the code you need to add before you call `ReactNativeBrownfield.initialize`:
```kt
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
loadReactNative(application)
-RNBrownfield.initialize(application, packages)
+ReactNativeBrownfield.initialize(application, packages)
```
@@ -45,15 +45,15 @@ A function used to initialize a React Native Brownfield singleton. Keep in mind
Params:
-| Param | Required | Type | Description |
-| ----------------------- | -------- | -------------------- | --------------------------------------------------------- |
-| application | Yes | `Application` | Main application. |
-| rnHost | No* | `ReactNativeHost` | An instance of [ReactNativeHost](https://bit.ly/2ZnwgnA). |
-| packages | No* | `List` | List of your React Native Native modules. |
-| options | No* | `HashMap` | Map of initial options. __Options listed below.__ |
-| onJSBundleLoaded | No* | `OnJSBundleLoaded` | Callback invoked after JS bundle is fully loaded. |
+| Param | Required | Type | Description |
+| ---------------- | ------------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| application | Yes | `Application` | Main application. |
+| reactHost | Exclusively* | `ReactHost` | An instance of [ReactHost](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt). |
+| packages | Exclusively* | `List` | List of your React Native Native modules. |
+| options | Exclusively* | `HashMap` | Map of initial options. __Options listed below.__ |
+| onJSBundleLoaded | Exclusively* | `OnJSBundleLoaded` | Callback invoked after JS bundle is fully loaded. |
-> * - Those fields aren't itself required, but at least one of them is. See examples below.
+> `*` - From the marked fields, exactly one must be specified, excluding the others. See examples below.
Available options:
- `useDeveloperSupport`: `Boolean` - Flag to use dev support.
@@ -81,7 +81,7 @@ val mReactNativeHost = object : ReactNativeHost(application) {
ReactNativeBrownfield.initialize(this, mReactNativeHost)
-OR
+// OR
ReactNativeBrownfield.initialize(this, mReactNativeHost) {
// onJSBundleLoaded
@@ -93,7 +93,7 @@ val packages = PackageList(this).getPackages()
ReactNativeBrownfield.initialize(this, packages)
-OR
+// OR
ReactNativeBrownfield.initialize(this, packages) {
// onJSBundleLoaded
@@ -109,7 +109,7 @@ val options = hashMapOf(
ReactNativeBrownfield.initialize(this, options)
-OR
+// OR
ReactNativeBrownfield.initialize(this, options) {
// onJSBundleLoaded
@@ -132,9 +132,9 @@ ReactNativeBrownfield.shared
**Properties:**
-| Property | Type | Default | Description |
-| --------------- | --------------- | -------------- | --------------------------------------------------------- |
-| reactNativeHost | `ReactNativeHost` | null | An instance of [ReactNativeHost](https://bit.ly/2ZnwgnA). |
+| Property | Type | Default | Description |
+| --------- | ----------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| reactHost | `ReactHost` | null | An instance of [ReactHost](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactHost.kt). |
---
@@ -142,16 +142,16 @@ ReactNativeBrownfield.shared
`createView`
-Creates a React Native view with a given module name. It automatically uses an instance of React Native created in `startReactNative` method. This is useful when embedding React Native views directly in your native layouts or Jetpack Compose UI.
+Creates a React Native view with a given module name. It automatically uses an instance of React Native created in `initialize` method. This is useful when embedding React Native views directly in your native layouts or Jetpack Compose UI.
Params:
-| Param | Required | Type | Description |
-| -------------- | -------- | ------------------- | ----------------------------------------------------------- |
-| context | Yes | `Context` | Android context to create the view |
-| activity | No | `FragmentActivity` | Activity hosting the view, used for lifecycle management |
-| moduleName | Yes | `String` | Name of React Native component registered to `AppRegistry` |
-| launchOptions | No | `Bundle` | Initial properties to be passed to React Native component |
+| Param | Required | Type | Description |
+| ------------- | -------- | ------------------ | ---------------------------------------------------------- |
+| context | Yes | `Context` | Android context to create the view |
+| activity | No | `FragmentActivity` | Activity hosting the view, used for lifecycle management |
+| moduleName | Yes | `String` | Name of React Native component registered to `AppRegistry` |
+| launchOptions | No | `Bundle` | Initial properties to be passed to React Native component |
Returns:
`FrameLayout` - A view containing the React Native component.
@@ -182,7 +182,7 @@ AndroidView(
#### `ReactNativeFragment`
-An fragment rendering `ReactRootView` with a given module name. It automatically uses a instance of React Native created in `startReactNative` method. It works well with exposed JavaScript module. All the lifecycles are proxied to `ReactInstanceManager`. It's the simplest way to embed React Native into your navigation stack.
+An fragment rendering `ReactRootView` with a given module name. It automatically uses a instance of React Native created in `initialize` method. It works well with exposed JavaScript module. All the lifecycles are proxied to `ReactInstanceManager`. It's the simplest way to embed React Native into your navigation stack.
```kotlin
import com.callstack.reactnativebrownfield.ReactNativeFragment
@@ -198,10 +198,10 @@ Creates a Fragment with `ReactNativeActivity`, you can use it as a parameter in
Params:
-| Param | Required | Type | Description |
-| ----------------------- | -------- | ------------------------------------------- | ----------------------------------------------------------- |
-| moduleName | Yes | `String` | Name of React Native component registered to `AppRegistry`. |
-| initialProps | No | `Bundle` \|\| `HashMap` \|\| `ReadableMap` | Initial properties to be passed to React Native component. |
+| Param | Required | Type | Description |
+| ------------ | -------- | ----------------------------------------------------- | ----------------------------------------------------------- |
+| moduleName | Yes | `String` | Name of React Native component registered to `AppRegistry`. |
+| initialProps | No | `Bundle` \|\| `HashMap` \|\| `ReadableMap` | Initial properties to be passed to React Native component. |
Examples:
@@ -229,6 +229,25 @@ map.putInt("score", 12)
ReactNativeFragment.createReactNativeFragment("ReactNative", map)
```
+### Usage in Jetpack Compose
+
+You can easily wrap the `ReactNativeFragment` inside a `AndroidFragment` composable to integrate React Native into your Jetpack Compose application. Since the AndroidFragment itself acts as a factory for the given Fragment class, you can pass the required arguments using a Bundle.
+
+The `arguments` passed to the `AndroidFragment` match the ones that can be passed to the `ReactNativeFragment.createReactNativeFragment` factory, yet need to be packed inside a Bundle, as follows:
+- ***(required)*** the JS component name, a `String` under the `ReactNativeFragmentArgNames.ARG_MODULE_NAME` (equivalent to `"arg_module_name"`) constant
+- *(optional)* the initial properties, a `Bundle` under the `ReactNativeFragmentArgNames.ARG_LAUNCH_OPTIONS` (equivalent to `"arg_launch_options"`) constant
+
+```kotlin
+import androidx.fragment.compose.AndroidFragment
+
+import com.callstack.reactnativebrownfield.constants.ReactNativeFragmentArgNames
+
+AndroidFragment(arguments = Bundle().apply {
+ putString(ReactNativeFragmentArgNames.ARG_MODULE_NAME, "ReactNative")
+ putBundle(ReactNativeFragmentArgNames.ARG_LAUNCH_OPTIONS, initialProps)
+}, modifier = Modifier.fillMaxSize())
+```
+
---
### Example
diff --git a/example/index.tsx b/example/App.tsx
similarity index 94%
rename from example/index.tsx
rename to example/App.tsx
index c4388644..8c52685f 100644
--- a/example/index.tsx
+++ b/example/App.tsx
@@ -1,5 +1,5 @@
import React, { useEffect } from 'react';
-import { AppRegistry, StyleSheet, Text, View, Button } from 'react-native';
+import { StyleSheet, Text, View, Button } from 'react-native';
import {
createNativeStackNavigator,
type NativeStackScreenProps,
@@ -71,7 +71,7 @@ type RootStackParamList = {
const Stack = createNativeStackNavigator();
-function App() {
+export default function App() {
return (
@@ -93,5 +93,3 @@ const styles = StyleSheet.create({
margin: 10,
},
});
-
-AppRegistry.registerComponent('ReactNative', () => App);
diff --git a/example/index.js b/example/index.js
new file mode 100644
index 00000000..64110690
--- /dev/null
+++ b/example/index.js
@@ -0,0 +1,5 @@
+import { AppRegistry } from 'react-native';
+
+import App from './App';
+
+AppRegistry.registerComponent('ReactNative', () => App);