Bringing use_frameworks!
to the New Architecture
#115
Replies: 5 comments 1 reply
-
Edited |
Beta Was this translation helpful? Give feedback.
-
Heads up that we introduced another breaking change for use frameworks and to break circular dependencies. + `spec.dependency React-NativeModulesApple` to your |
Beta Was this translation helpful? Give feedback.
-
I missed one important change you need to carry on in your Components libraries. In all the files importing the - #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. |
Beta Was this translation helpful? Give feedback.
-
Thank you @cipolleschi for providing this great document. The created by @atlj has been answered here |
Beta Was this translation helpful? Give feedback.
-
What's the current state of dynamic frameworks, not being compatible with the default configuration of an iOS framework seems problematic. |
Beta Was this translation helpful? Give feedback.
-
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 theinstall_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:
use_frameworks! :linkage => :static
(which generates a project with Frameworks that are statically linked)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:
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:
before the invocation of the
use_react_native!
function. In that case, theuse_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 toinstall_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 theHEADER_SEARCH_PATHS
property of yourpodspec
’spod_target_xcconfig
:The following changes are automagically applied by Cocoapods
podspec
depends onRCTThirdPartyFabricComponentProvider
(Note that it shouldn’t)#import <React/RCTThirdPartyFabricComponentProvider.h>
react/renderer/imagemanager/platform/ios
folder:spec.dependency React-ImageManager
to your dependencies${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/imagemanager/platform/ios
to yourHEADER_SEARCH_PATHS
property of your podspec’spod_target_xcconfig
spec.dependency React-NativeModulesApple
to your librarypodspec
file.If your library access some files in theReact-RCTFabric
pod (any header in theReact/Fabric
folder) add the following line to theHEADER_SEARCH_PATHS
property of your podspec’spod_target_xcconfig
:${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers
For App Developers that are not using the
RCTAppDelegate
class:RCTAppDelegate
, as it will protect you from future changes.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:
ReactCommon/react/renderer
folder of the framework. The podspec file and the pod name for the C++ code of Fabric is calledReact-Fabric
React/Fabric
folder of the repository. The podspec file and the pod name for the iOS specific code of Fabric is calledReact-RCTFabric
React-Codegen
and the code is usually inside the<appName>/ios/build
folder in the final app. Codegen emits also some core components in thereact/renderer/components/rncore
folder and it emits anRCTThirdPartyFabricComponentProvider
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 thePodfile
or to install the dependencies with theNO_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 theReact-Codegen
library, which needs access to some base classes inReact-Fabric
in order to generate some valid code (for example, it needs access to the headers contained in thereact/renderer/components/view
namespace). However, some other components from theReact-Fabric
framework needs to access theReact-Codegen
framework (for example: theSafeAreaViewShadowNode
). 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 theReact-RCTFabric
pod: the podspec for this pod specify anheader_dir
ofReact
and amodule_name
ofRCTFabric
.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, whenuse_frameworks!
is turned on, to import a file from theReact-RCTFabric
framework we need to use the syntax#import <RCTFabric/header.h>
. However, currently, all the files that refer to an header in theReact-RCTFabric
pod are usingReact
as the framework name, as reflected by thepodspec
’sheader_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, thereact/renderer/graphics
folder structure was something like this: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 theplatform/ios
folder to the new one.For the example above, the new folder structure becomes the following:
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 thereact/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 thereact/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 theinclude_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 theinclude_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 yourpodspec
’sHEADER_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 fromReact-RCTFabric
and thatReact-RCTFabric
was using, and some platform code for Turbomodules, which lives inReactCommon
and that requires some files fromReact-Core
, whileReact-Core
depends onReactCommon
.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 theReactCommon/react/renderer/components/rncore
folderRCTThirdPartyFabricProvider
is now generated directly inside theReact-RCTFabric
folder.There were two circular dependency due to platform specific code: one introduced by
RCTAppSetupUtils
and one by thereact/renderer/ImageManager
.The former dependency was caused by
RCTAppSetupUtils.h
that is importing theRTCTurboModuleManager.h
. This class lives inReactCommon
soReact-Core
was depending onReactCommon
. However, the platform-specific classes in thenativemodule
package ofReactCommon
depends onReact-Core
because the need to access theRCTBridge
. So,ReactCommon
was actually depending onReact-Core
. We break that dependency by moving the theRCTAppSetupUtils
to theRCTAppDelegate
pod, so now the dependency is broken.To fix the latter, we created a new
podspec
calledReact-ImageManager
that contains theImageManager
platform-specific code. This pod depends onReact-Core
but not onReact-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 theReactCommon/react/nativemodule/platform/ios
. By splitting ReactCommon in two separate pods, we can now have thatReactCommon
does not depends on anything,React-Core
depends only onReactCommon
and the newreact-NativeModulesApple
depends on both pods.Breaking Changes
If you are an app developer,
RCTAppDelegate
class, this change is not a breaking change at all.#import
at the top of yourAppDelegate
to reference the new path for theRCTAppSetupUtils
.#import <RCTAppSetupUtils.h>
If you are a library developer:
RCTThirdPartyFabricProvider
file, you should update the include and import statements with the<React/
suffix.imageManager/platform/ios
folder, you should add the new dependency to your podspec file. Specifically, you would need to add:spec.dependency React-ImageManager
spec.dependency 'React-NativeModulesApple' to your library
podspec` file.We strongly suggest you to migrate to the
RCTAppDelegate
and to theinclude_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 thePUBLIC_HEADERS_FOLDER_PATH
property of thexcconfig
.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 theReact/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 theinclude_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 someRCTFabric
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.
Beta Was this translation helpful? Give feedback.
All reactions