Adding React Native to iOS app

React Native Enterprise Framework helps you package your React Native code into files that your iOS and Android apps can use. For iOS, it creates a .xcframework file that you can easily add to your app.

To add React Native to your iOS app, we'll package your React Native code into an XCFramework. This way, you don't need to set up Node.js or CocoaPods in your main app.

To make the integration simpler and more powerful, we'll use the React Native Brownfield library.

Creating a New RNEF Project (Automated)

If you are creating a new RNEF project, you can select the brownfield-ios plugin to add brownfield capabilities, install dependencies, and jump to 6. Create the XCFramework step.

Integrating to An Existing RNEF Project (Manual)

If you have an existing RNEF project, follow the instructions below.

1. Create a New Framework in React Native app's Xcode:

  1. Open your React Native project's ios/<project_name>.xcworkspace in Xcode

  2. Add a new target by clicking File > New > Target

  3. Choose the Framework template Framework Target

  4. Give your framework a unique name. You'll use this name when adding it to your main app

  5. Right-click the framework folder and select Convert to Group. CocoaPods doesn't work properly with references. Perform this step for both <FrameworkName> and <FrameworkName>Tests folders. The menu that appears when user right clicks on the generated framework folder

  6. Set these build settings for your framework:

    Build SettingValueWhat it does
    Build Libraries for DistributionYESCreates a module interface for Swift. Also checks if the framework works with your Xcode version.
    User Script SandboxingNOLets scripts modify files, which we need to create the JavaScript bundle.
    Skip InstallNOMakes sure Xcode creates the framework files we need.
    Enable Module VerifierNOSkips testing the framework during build, which makes builds faster.

2. Update CocoaPods in your React Native app:

  1. Add your new framework to ios/Podfile:

    Podfile
    target '<project_name>' do
      # Add these lines
      target '<framework_target_name>' do
        inherit! :complete
      end
    end

3. Add the Bundle Script:

  1. In Xcode, click on your React Native app target
  2. Go to Build Phases
  3. Find the Bundle React Native code and images step Bundle React Native code and images build phase
  4. Copy the script from there
  5. Click on your framework target
  6. Go to Build Phases
  7. Click the + button and choose New Run Script Phase New Run Script Phase
  8. Paste the script you copied
  9. Name the phase Bundle React Native code and images
  10. Add these files to the script's input files:
    • $(SRCROOT)/.xcode.env.local
    • $(SRCROOT)/.xcode.env

4. Create the Framework's Public Interface:

  1. Install React Native Brownfield library:
    npm
    yarn
    pnpm
    bun
    npm install @callstack/react-native-brownfield
  2. Create a new Swift file in your framework folder using Xcode with the following contents:
    // Export helpers from @callstack/react-native-brownfield library
    @_exported import ReactBrownfield
    // Initializes a Bundle instance that points at the framework target.
    public let ReactNativeBundle = Bundle(for: InternalClassForBundle.self)
    class InternalClassForBundle {}

5. Setup RNEF's brownfield plugin:

  1. Add @rnef/plugin-brownfield-ios to your React Native project

    npm
    yarn
    pnpm
    bun
    npm install @rnef/plugin-brownfield-ios
  2. Add this to your rnef.config.mjs:

    rnef.config.mjs
    import { pluginBrownfieldIos } from '@rnef/plugin-brownfield-ios';
    
    export default {
      plugins: [
        pluginBrownfieldIos(),
        // ...
      ],
    };

6. Create the XCFramework

Run the following command in Terminal:

Terminal
rnef package:ios --scheme <framework_target_name> --configuration Release

7. Add the Framework to Your iOS App:

  1. Open the .rnef/cache/ios/package directory and drag the following files into your app's Xcode project (you can select all at once):

    .rnef/cache/ios/package
    ├── hermes.xcframework                  # JavaScript runtime
    ├── ReactBrownfield.xcframework         # React Native Brownfield library
    └── <framework_target_name>.xcframework # Your framework target

    Linked frameworks in Xcode sidebar

    INFO

    Xcode builds depend on files being referenced in the project file. If you create a new source file without using Xcode, your file is not going to be referenced by the project.

  2. Initialize React Native in AppDelegate.swift and show it using ReactNativeViewController available from React Native Brownfield library:

    AppDelegate.swift
    import <framework_target_name>
    
    @main
    class AppDelegate: UIResponder, UIApplicationDelegate {
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            // Initialize React Native
            ReactNativeBrownfield.shared.bundle = ReactNativeBundle
            ReactNativeBrownfield.shared.startReactNative(onBundleLoaded: {
                print("React Native bundle loaded")
            }, launchOptions: launchOptions)
    
            // Add `window` property required by React Native
            window = UIWindow(frame: UIScreen.main.bounds)
    
            // Create VC that calls your module by name
            let reactNativeVC = ReactNativeViewController(moduleName: "Enterprise")
    
            // Display the view as full window or anyhow you need
            window?.rootViewController = reactNativeVC
            window?.makeKeyAndVisible()
        }
    NOTE

    Instead of using AppDelegate you can also use SceneDelegate

    8. Run your native iOS App

    Now that you have everything set up, you can run your app in Debug or Release configuration, and it will display a React Native app we just packaged with package:ios command.

    Debug configuration

    When running in Debug, React Native Brownfield will expect you to run a JS dev server.

    You can do so by running start command in your Terminal app:

    npx rnef start

    Release configuration

    When running in Release, React Native Brownfield will load the JS bundle (main.jsbundle) directly from the release XCFramework file without requiring you to run any JavaScript tooling.