Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Discussion options

TL;DR: the previous structure of Fabric was preventing us from using frameworks with the New Architecture. We lined up a set of changes for 0.72 that will make it possible to run the New Architecture using Static Frameworks. This change comes with breaking changes that could affect both app developers and library authors that are not using yet the RCTAppDelegate and the install_modules_dependencies function delivered in 0.71.

Right now, the React Native New Architecture on iOS can only be built as a static library. This will be limiting as a set of Open Source libraries requires React Native to be built as framework. React Native Firebase being a popular library (with a weekly download count in the order of ~20% of React Native's one) was badly affected by this. This is one library with that requirement, and surely there are more libraries out there that requires that. We believe this is a blocker to deliver a stable experience with the New Architecture.

Note that the Old Architecture supports use_frameworks in all its alternatives:

  • You can build the Old Architecture without specifying use_frameworks! (which ends up in building Static Libraries)
  • You can build the Old Architecture using use_frameworks! :linkage => :static (which generates a project with Frameworks that are statically linked)
  • You can build the Old Architecture using use_frameworks! :linkage => :dynamic (which generates a project with Frameworks that are dynamically linked)

How to use it

The suggested way enable the New Architecture with Static Frameworks is to reinstall the Cocoapods of your app by running the command:

USE_FRAMEWORKS=static RCT_NEW_ARCH_ENABLED=1 bundle exec pod install 

This will set up the project with the dependencies installed as Frameworks that are statically linked.
Notice that the same syntax can be used to install the dependencies for the Old Architecture (by omitting the RCT_NEW_ARCH_ENABLED=1 flag).

Alternatively, you can just modify your Podfile adding this line:

use_frameworks! :linkage => :static

before the invocation of the use_react_native! function. In that case, the use_react_native! function can inspect the target settings for your app and install the dependencies in the right way.

Important

Currently, use_frameworks! with dynamic linking is not supported yet for the New Architecture.

List Of Breaking Changes

EDITED: We recently shipped some changes that improves the situation w.r.t. breaking changes. I'll leave them here but I'll cross the ones that should be already fixed by our scripts.

For Library developers that are not using install_modules_dependencies:

  • If you can, migrate to install_modules_dependencies as it will protect you from following changes and you won’t have to apply the following changes.
  • Otherwise, add the following lines to the HEADER_SEARCH_PATHS property of your podspec’s pod_target_xcconfig:
The following changes are automagically applied by Cocoapods
+ use_frameworks_headers = [
+  "$(PODS_ROOT)/DoubleConversion",
+  "$(PODS_ROOT)/RCT-Folly",
+  "$(PODS_CONFIGURATION_BUILD_DIR)/React-Fabric/React_Fabric.framework/Headers",
+  "$(PODS_CONFIGURATION_BUILD_DIR)/React-graphics/React_graphics.framework/Headers",
+  "$(PODS_CONFIGURATION_BUILD_DIR)/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios",
+  "$(PODS_CONFIGURATION_BUILD_DIR)/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
+  "$(PODS_CONFIGURATION_BUILD_DIR)/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core/platform/ios",
+  "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/textinput/iostextinput"
+ ].map{|p| "\"#{p}\""}.join(" ") 

# ....

spec.pod_target_xconfing = {
    "HEADER_SEARCH_PATH" => "<your previous search path>" + 
+   ENV['USE_FRAMEWORKS'] != nil ? " " + use_frameworks_headers : ""   
}
  • If your podspec depends on RCTThirdPartyFabricComponentProvider (Note that it shouldn’t)
    • Update the import statement to #import <React/RCTThirdPartyFabricComponentProvider.h>
  • If your pod makes use of any of the headers in the react/renderer/imagemanager/platform/ios folder:
    • Add the dependency: spec.dependency React-ImageManager to your dependencies
    • Add the ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/imagemanager/platform/ios to your HEADER_SEARCH_PATHS property of your podspec’s pod_target_xcconfig
  • If your library supports iOS and makes use of TurboModules, you may have to add the spec.dependency React-NativeModulesApple to your library podspec file.
  • If your library access some files in the React-RCTFabric pod (any header in the React/Fabric folder) add the following line to the HEADER_SEARCH_PATHS property of your podspec’s pod_target_xcconfig:
    • ${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers

For App Developers that are not using the RCTAppDelegate class:

  • If you can, migrate to RCTAppDelegate, as it will protect you from future changes.
  • Otherwise, if you are accessing directly to the RCTAppSetupUtils file, you need to update the #import statements to #import <RCTAppSetupUtils.h>.

Basic Concepts

When we talk about Fabric, we are talking about more than just a single pod. The Fabric ecosystem on iOS is quite complex and we can use some terminology to align on what we are talking about in this post only:

  • Fabric: when we talk about Fabric as part of the code, we are referring to the C++ shared code that is contained in the ReactCommon/react/renderer folder of the framework. The podspec file and the pod name for the C++ code of Fabric is called React-Fabric
  • RCTFabric: Fabric needs to talk with the Native platform. To do so, it needs the support of some platform specific code. For iOS, that code is contained in the React/Fabric folder of the repository. The podspec file and the pod name for the iOS specific code of Fabric is called React-RCTFabric
  • Codegen: Codegen is one of the pillars of the New Architecture and React Native makes use of it extensively. For what concerns iOS, the code emitted by Codegen is grouped together into a pod called React-Codegen and the code is usually inside the <appName>/ios/build folder in the final app. Codegen emits also some core components in the react/renderer/components/rncore folder and it emits an RCTThirdPartyFabricComponentProvider in the rood of the Codegen package.

Other interesting dependencies which broke use_frameworks!

Historically, use_frameworks! has been broken by other two important parts of React Native: Hermes and Flipper.

For what concern Hermes, the JS engine is now shipped as a prebuilt framework in the apps, so it now supports use_frameworks! out-of-the-box.

Flipper is still broken, unfortunately. So, if you want to run your app with use_frameworks!, remember to either disable flipper manually in the Podfile or to install the dependencies with the NO_FLIPPER=1 flag before the pod install command.

Why Fabric does not support use_frameworks!?

Frameworks are a specific way to bundle together code and other resources like localization strings, images and so on. If the code bundled in a framework is statically linked (a static library) we call them static frameworks. If the code bundled in a framework is a dynamic library we call them dynamic frameworks.

Moreover, frameworks have specific rules that needs to be respected, in order to work properly. For example, they have a specific folder structure and Headers should be put in the right folders to leverage the automatic lookup mechanism.

When Cocoapods install the dependencies with the use_frameworks! directive, it applies some transformation and some build rules to make sure that the result of the compilation process will be a proper framework. Part of these build rules flattens the Objective-C and C++ headers into the Headers folder of the framework.

Fabric, on the other hand, makes large use of namespaced #include statements. For example, to include something from the core of the Fabric renderer, the #include structure usually follows this syntax: #include <react/renderer/core/header.h>. This namespaced import style is not allowed by default by iOS Frameworks.

Another reason why it was not possible to run use_frameworks! was the presence of some Circular Dependencies in between Fabric and Codegen: Codegen is used to generate the boilerplate code for some components that are shipped with React Native. However, this code is generated in the React-Codegen library, which needs access to some base classes in React-Fabric in order to generate some valid code (for example, it needs access to the headers contained in the react/renderer/components/view namespace). However, some other components from the React-Fabric framework needs to access the React-Codegen framework (for example: the SafeAreaViewShadowNode). This creates a circular dependency that creates some compilation troubles.

Finally, the third problem for which we could not enable use_frameworks! without some work is due to the React-RCTFabric pod: the podspec for this pod specify an header_dir of React and a module_name of RCTFabric.
The module_name is the property used by Cocoapods to actually name the Framework. The name of the framework is part of the #import or #include syntax, so, when use_frameworks! is turned on, to import a file from the React-RCTFabric framework we need to use the syntax #import <RCTFabric/header.h>. However, currently, all the files that refer to an header in the React-RCTFabric pod are using React as the framework name, as reflected by the podspec’s header_dir parameter.

How do we solve it?

In the following, we are going to explain how we solved every specific problem and whether it will be a breaking change or not.

Header Flattening

The flattening of the Headers can be solved by leveraging the Cocoapods’ header_mappings_dir property of the podspecs.
When this is specified, the folder structure in a module is preserved, but there is no way to force it to stop the mapping at a given point: all the folders presents starting from the value of header_mappings_dir are preserved. So, for example, the react/renderer/graphics folder structure was something like this:

* react/renderer/graphics
    * platform/ios
        * file1.h
        * file1.mm
        * ...
    * file2.h
    * file2.cpp

To provide the same include path for the files contained in some platform-specific folders, we recreated the same folder path in the platform/ios subfolder and moved the file from the platform/ios folder to the new one.

For the example above, the new folder structure becomes the following:

* react/renderer/graphics
    * platform/ios/react/renderer/graphics
        * file1.h
        * file1.mm
        * ...
    * file2.h
    * file2.cpp

This change is a stepping stone to actually fix the problem. The second step was to update the search paths when use_frameworks! is enabled. Thanks to this structure, we can instruct Cocoapods and Xcode to lookup for the headers in the following spaces:

  • React-graphics/React_graphics.framework/Headers → this allows the code to find all the headers in the react/renderer/graphics folder and to include them with #include <react/renderer/graphics/header.h>.
  • React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios → this allows the code to find all the headers in the react/renderer/graphics/platform/ios folder and to include them with #include <react/renderer/graphics/header.h>, the same include path.

Breaking Change:

This change allow you not to change any of the #include or #import statements in your code.
If you are an app developer, this change should not be a breaking change at all.
If you are a library developer:

  • If you are using the include_modules_dependencies function we introduced in 0.71, this change is not a breaking change as we updated that function to set up the search path correctly.
  • If you are not using that function, this change is breaking and you should update the search path of your podspec with the proper search paths.

We strongly suggest you to migrate to the include_modules_dependencies function as it will make the migration much more easier and will protect you from future changes.

Changed folders:
You should add this line in your podspec’s HEADER_SEARCH_PATH:

  • ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers

Plus, this is the list of folders that has been updated following this pattern, and for which you’ll need to update the search path:

  • ReactCommon/react/renderer/graphics/platform/ios. The search paths to add are:
    • $(PODS_CONFIGURATION_BUILD_DIR)/React-graphics/React_graphics.framework/Headers
    • $(PODS_CONFIGURATION_BUILD_DIR)/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios
  • ReactCommon/react/nativemodules/core/platform/ios. The search paths to add are:
    • $(PODS_CONFIGURATION_BUILD_DIR)/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core
    • $(PODS_CONFIGURATION_BUILD_DIR)/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core/platform/ios
  • ReactCommon/react/renderer/components/textinput/iostextinput/. The search path to add is:
    • ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/textinput/iostextinput
  • ReactCommon/react/renderer/textlayoutmanager/. The search path to add is:
    • ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/textinput/iostextinput

Cocoapods should take care of update your search paths automatically.

Circular Dependencies

There were 3 kind of circular dependencies to break: one was generated by the Codegen, with files in a framework depending on files in another framework and viceversa, some platform code which was present in ReactCommon which required files from React-RCTFabric and that React-RCTFabric was using, and some platform code for Turbomodules, which lives in ReactCommon and that requires some files from React-Core, while React-Core depends on ReactCommon.

The circular dependencies introduced by Codegen has been fixed by generating the offending files in some other places. Specifically:

  • rncore components are now generated inside the ReactCommon/react/renderer/components/rncore folder
  • the RCTThirdPartyFabricProvider is now generated directly inside the React-RCTFabric folder.

There were two circular dependency due to platform specific code: one introduced by RCTAppSetupUtils and one by the react/renderer/ImageManager.

The former dependency was caused by RCTAppSetupUtils.h that is importing the RTCTurboModuleManager.h. This class lives in ReactCommon so React-Core was depending on ReactCommon. However, the platform-specific classes in the nativemodule package of ReactCommon depends on React-Core because the need to access the RCTBridge. So, ReactCommon was actually depending on React-Core. We break that dependency by moving the the RCTAppSetupUtils to the RCTAppDelegate pod, so now the dependency is broken.

To fix the latter, we created a new podspec called React-ImageManager that contains the ImageManager platform-specific code. This pod depends on React-Core but not on React-RCTFabric, thus the latter can use it freely.

Finally, to solve the circular dependency caused by TurboModules, we created a separate podspec for platform specific code in the ReactCommon/react/nativemodule/platform/ios. By splitting ReactCommon in two separate pods, we can now have that ReactCommon does not depends on anything, React-Core depends only on ReactCommon and the new react-NativeModulesApple depends on both pods.

Breaking Changes
If you are an app developer,

  • if you are using the RCTAppDelegate class, this change is not a breaking change at all.
  • otherwise, you need to change the #import at the top of your AppDelegate to reference the new path for the RCTAppSetupUtils.
    • use #import <RCTAppSetupUtils.h>

If you are a library developer:

  • If you are not using that function, this change could be breaking.
    • If you are accessing directly the RCTThirdPartyFabricProvider file, you should update the include and import statements with the <React/ suffix.
    • If you were referring to some of the files in the imageManager/platform/ios folder, you should add the new dependency to your podspec file. Specifically, you would need to add:
      • spec.dependency React-ImageManager
  • If you are maintaining a turbomodule, you may need to add the spec.dependency 'React-NativeModulesApple' to your library podspec` file.

We strongly suggest you to migrate to the RCTAppDelegate and to the include_modules_dependencies function as it will make the migration much more easier and will protect you from future changes.

RCTFabric

To solve the problem with RCTFabric, we have to force Cocoapods to generate the Headers into a folder called React. This has been achieved by updating the PUBLIC_HEADERS_FOLDER_PATH property of the xcconfig.
However, this change prevent anything from actually access those headers naturally. So, we had to update the search paths for the RCTFabric module using the proper path: ${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers.

Breaking Changes
~This change will preserve the #import <React/header.h> syntax for the headers in the React/Fabric folder. ~
If you are an app developer, this change should not be a breaking change at all.
If you are a library developer:

  • If you are using the include_modules_dependencies function we introduced in 0.71, this change is not a breaking change as we updated that function to set up the search path correctly.
  • if you are not using that function, this change is breaking if your library was referring to some RCTFabric header. You should update the search path of your podspec with the proper search paths:
    • ${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers

Cocoapods should take care of update your search paths automatically.

You must be logged in to vote

Replies: 5 comments · 4 replies

Comment options

Edited
We landed some changes to reduce the amount of breaking changes.
I left the previous list to keep an historical track of them.

You must be logged in to vote
0 replies
Comment options

Heads up that we introduced another breaking change for use frameworks and to break circular dependencies.
Specifically, if your library is a Turbo Native Module, it is likely that you have to add the following line:

+ `spec.dependency React-NativeModulesApple`

to your podspec file.

You must be logged in to vote
0 replies
Comment options

I missed one important change you need to carry on in your Components libraries.

In all the files importing the react/renderer/graphics/conversions.h file, you should apply the following diff:

- #include <react/renderer/graphics/conversions.h>
+ #include <react/renderer/core/graphicsConversions.h> 

This is not required immediately by 0.72, but will be required by 0.73.

You must be logged in to vote
0 replies
Comment options

Thank you @cipolleschi for providing this great document. The issue created by @atlj has been answered here

You must be logged in to vote
0 replies
Comment options

What's the current state of dynamic frameworks, not being compatible with the default configuration of an iOS framework seems problematic.

You must be logged in to vote
4 replies
@cipolleschi
Comment options

cipolleschi Dec 13, 2024
Maintainer Author

What do you mean? React Native support static libraries, static frameworks and dynamic frameworks.

We have tests in CI on both our example app and the app created with the template for the 2 extremes: static libraries and dynamic frameworks.

For example:

@ChristianGoodridgeLVM
Comment options

@cipolleschi I am referring to this

@cipolleschi
Comment options

cipolleschi Jan 20, 2025
Maintainer Author

That’s pretty old.. the New Arch supports dynamic linking since 0.73 or 0.74, IIRC. We are supporting them for more than a year now.

@cipolleschi
Comment options

cipolleschi Jan 20, 2025
Maintainer Author

As I shown above, we have multiple CI jobs that are running with dynamic frameworks to ensure that they don’t break

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.