Skip to content

ry-allan/expo-brownfield-target

 
 

Repository files navigation

expo-brownfield-target by Software Mansion

Warning

This library is in early development stage; breaking changes can be introduced in minor version upgrades.

expo-brownfield-target

expo-brownfield-target is a library which includes an Expo config plugin that automates brownfield setup in the project, CLI for building the brownfield artifacts and built-in APIs for communication and navigation between the apps.

📖 Documentation

Table of contents

Motivation

Brownfield approach enables integrating React Native apps into native Android and iOS projects, but setting it up, especially in Expo projects using Continuous Native Generation is a manual, repetitive, and pretty complex task.

This library aims to fully automate and simplify brownfield setup by including a config plugin that configures your project on every prebuild, built-in APIs for common use cases and CLI which builds the brownfield artifacts.

Such setup of brownfield allows for easy publishing to Maven, as XCFramework or using Swift Package Manager which enables e.g. simple and more independent cooperation of native and RN teams.

Features

  • Automatic extension of native projects with brownfield targets
  • Easy integration with Expo project via config plugin interface
  • Artifact publishing using XCFramework for iOS and Maven for Android
  • Configurability & customizability
  • APIs for bi-directional communication and navigation between the apps

Platform & Expo SDK compatibility

The plugin supports both Android and iOS. As of now we only support Expo SDK 54.

Usage

Installation

npm install expo-brownfield-target

Plugin setup

Add the config plugin to the "plugins" section in your app.json or app.config.js / app.config.ts:

{
  "expo": {
    "name": "my-awesome-expo-project",
    ...
    "plugins": [
      ... // Other plugins
      "expo-brownfield-target"
    ]
  }
}

If you want to pass any configuration options make sure to add the plugin as an array:

{
  "expo": {
    "name": "my-awesome-expo-project",
    ...
    "plugins": [
      ... // Other plugins
      [
        "expo-brownfield-target",
        {
          "android": {
            ...
          },
          "ios": {
            ...
          }
        }
      ]
    ]
  }
}

See configuration.md for full reference of configurable options.

Manual setup

All steps performed by the plugin can also be performed manually. Please refer to manual-setup.md for a full guide for manual setup.

Adding brownfield targets

The additional targets for brownfield will be added automatically every time you prebuild the native projects:

npx expo prebuild

Building with CLI

The plugin comes with a built-in CLI which can be used to build both Android and iOS targets:

npx expo-brownfield-target build-android -r MavenLocal

npx expo-brownfield-target build-ios

More details and full reference of the CLI commands and options can be found in cli.md.

Building manually

Brownfields can be also built manually using the xcodebuild and ./gradlew commands.

# Compile the framework
xcodebuild \
    -workspace "ios/myexpoapp.xcworkspace" \
    -scheme "MyBrownfield" \
    -derivedDataPath "ios/build" \
    -destination "generic/platform=iphoneos" \
    -destination "generic/platform=iphonesimulator" \
    -configuration "Release"

# Package it as an XCFramework
xcodebuild \
    -create-xcframework \
    -framework "ios/build/Build/Products/Release-iphoneos/MyBrownfield.framework" \
    -framework "ios/build/Build/Products/Release-iphonesimulator/MyBrownfield.framework" \
    -output "artifacts/MyBrownfield.xcframework"
./gradlew publishBrownfieldAllPublicationToMavenLocal

See publishing.md for more details about the publishing tasks.

Using built artifacts in native projects

Below snippets are taken from the examples of using brownfields inside native apps at: example/android, example/ios and example/ios-swiftui.

Android

// MainActivity.kt
package com.swmansion.example

import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
import com.swmansion.brownfield.showReactNativeFragment

class MainActivity : AppCompatActivity(), DefaultHardwareBackBtnHandler {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        showReactNativeFragment()
    }

    override fun invokeDefaultOnBackPressed() {
        // ...
    }
}

iOS (SwiftUI)

// ContentView.swift
import SwiftUI
import MyBrownfieldApp

struct ContentView: View {
    init() {
        ReactNativeHostManager.shared.initialize()
    }

    var body: some View {
        VStack {
            ReactNativeView(moduleName: "main")
        }
    }
}

iOS (UIKit)

// AppDelegate.swift
import UIKit
import MyBrownfieldApp

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(
      _ application: UIApplication,
      didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        ReactNativeHostManager.shared.initialize()

        window = UIWindow(frame: UIScreen.main.bounds)
        let viewController = ReactNativeViewController(moduleName: "main")
        window?.rootViewController = viewController
        window?.makeKeyAndVisible()

        return true
    }
}

Using with Metro Bundler

Debug builds use bundle hosted by Metro server (hosted over localhost:8081) instead of the bundle included in the brownfield framework.

Be sure to start Metro server by running the following command in your Expo project:

npm start

Android

To be able to use Metro create a separate debug-only Manifest with the following contents in your native app which will host the brownfield:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <application
        android:usesCleartextTraffic="true"
        tools:ignore="GoogleAppIndexingWarning"
        tools:replace="android:usesCleartextTraffic"
        tools:targetApi="28" />
</manifest>

Then be sure to build and publish the artifacts using either All (includes both debug and release) or Debug configuration and to use the debug variant in the native app. Also, don't forget to reverse the port 8081 (if necessary):

adb reverse tcp:8081 tcp:8081

iOS

To use Metro server instead of bundle included at the build time, compile the brownfield framework using Debug configuration (-d/--debug flag when using the CLI). Debug XCFramework should automatically source the bundle from the Metro server.

Acknowledgments

Huge thanks to:

expo-brownfield-target is created by Software Mansion

swm

Since 2012 Software Mansion is a software agency with experience in building web and mobile apps. We are Core React Native Contributors and experts in dealing with all kinds of React Native issues. We can help you build your next dream product – Hire us.

Made by @software-mansion and community 💙

About

Expo config plugin extending native projects for building app as a brownfield

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 54.3%
  • Kotlin 31.5%
  • Swift 7.9%
  • Shell 2.5%
  • JavaScript 2.3%
  • Ruby 1.5%