Skip to content

software-mansion-labs/expo-brownfield-target

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

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 an Expo config plugin that allows you to easily extend your Expo app with additional native targets, enabling you to build and distribute it as a brownfield project.

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 plugin aims to fully automate this process on every prebuild and provides a set of configurable file templates and a CLI which streamlines brownfield distribution. Additionally such setup of brownfield allows for easy packaging it as a fat-AAR, XCFramework or Swift Package which simplifies its shipping and enables e.g. simple and more independent cooperation of native and RN teams.

Features

  • Automatically adds native projects for building brownfield to your Expo app during prebuilds
  • Enables easy integration with the Expo project via config plugin interface
  • Enables building the brownfield as an XCFramework or an AAR which simplifies usage in the native projects
  • Customizable through file templates and the config plugin options
  • Supports navigating out of React Native view

Note: Our goal is maximum customizability, so if you feel like anything else needs to be customizable, please feel free to cut an issue.

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": { 
            ...
          }
        }
      ]
    ]
  }
}

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 --clean

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
npx expo-brownfield-target build-ios

More details and full reference of the CLI commands can be found below in the CLI Reference section.

Building manually

Brownfields can be also built manually using the xcodebuild and ./gradlew commands. Please see build-xcframework.sh and build-aar.sh for an example reference of manual building.

Using built artifacts in native projects

Below snippets are taken from the examples of using brownfields inside native apps at: /examples/android, /examples/ios and /examples/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
    }
}

Navigation

Methods

popToNative(animated?: boolean)

Description: A method to return to the native view which precedes the React Native brownfield in the navigation history.

Arguments:

Name Required Description Platform support Default value
animated No Specifies if the return to the native view should be performed with an animation iOS (UIKit) false

Example:

import * as ExpoBrownfieldModule from 'expo-brownfield-target';

...

<Button 
  title="Go back" 
  onPress={() => ExpoBrownfieldModule.popToNative()} 
/>

<Button 
  title="Go back animated" 
  onPress={() => ExpoBrownfieldModule.popToNative(true)} 
/>

CLI reference

CLI commands

build-android

Builds the Android library as a fat-AAR and copies it to the artifacts directory. By default it also publishes the AAR to local Maven repository.

npx expo-brownfield-target build-android [options]
Option Short option Description Default value
--help -h Displays help message for build-android -
--no-publish - Skips publishing the AAR to local Maven repo -
--tasks -t Enables running specified custom tasks sequentially after assembleDebug/assembleRelease (e.g. for custom Maven publishing flow). List should be specified in the following format: task1,task2,task3,task4 -
--debug -d Specifies to build the framework with Debug configuration. If both --debug and --release are passed --release takes precedence If no option for configuration is passed framework will be built in Release
--release -r Specifies to build the framework with Release configuration. If both options are passed --release takes precedence over --debug If no option for configuration is passed framework will be built in Release
--verbose - Output of all commands ran by the CLI (e.g. ./gradlew assembleRelease) will be printed in the terminal -
--artifacts -a Directory where built artifacts (XCFrameworks and AAR) should be placed ./artifacts (relative to the Expo project root)
--library -l The name of the Android library for brownfield brownfield

build-ios

Builds the iOS framework, packages it as an XCFramework and places it in the artifacts directory (artifacts/) along with the hermes.xcframework copied from Pods.

npx expo-brownfield-target build-ios [options]
Option Short option Description Default value
--help -h Displays help message for build-ios -
--scheme -s Scheme for brownfield target which should be build Scheme name automatically inferred from the native project
--xcworkspace -x Path to .xcworkspace file Path automatically inferred from the native project
--debug -d Specifies to build the framework with Debug configuration. If both --debug and --release are passed --release takes precedence If no option for configuration is passed framework will be built in Release
--release -r Specifies to build the framework with Release configuration. If both options are passed --release takes precedence over --debug If no option for configuration is passed framework will be built in Release
--verbose - Output of all commands ran by the CLI (e.g. ./gradlew assembleRelease) will be printed in the terminal -
--artifacts -a Directory where built artifacts (XCFrameworks and AAR) should be placed ./artifacts (relative to the Expo project root)

Configuration reference

Android

Property Description Default value
library Name of the Android library used for the brownfield brownfield
package Package identifier for the brownfield library android.package appended with .brownfield or com.example.brownfield if android.package is undefined

iOS

Property Description Default value
bundleIdentifier Bundle identifier for the brownfield native target. ios.bundleIdentifier with last component replaced with the target name or com.example.<target-name> if ios.bundleIdentifier is undefined.
targetName Name of the brownfield native target. Also used as the name of the directory containing brownfield files. The value is sanitized to only contain alphanumeric characters and start with a letter. config.scheme or config.ios.scheme appended with brownfield, if either value is defined and a single string. If not defaults to to <slug>brownfield, where <slug> is sanitized slug from the Expo project config

File templates

You can also overwrite the templates which are used to generate the files to even better suit the plugin behavior to your requirements. More information about overwriting the templates can be found in TEMPLATES.md.

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 ๐Ÿ’™