Bringing use_frameworks! to the New Architecture
#115
Replies: 5 comments 4 replies
-
|
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 |
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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
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
RCTAppDelegateand theinstall_modules_dependenciesfunction 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=1flag).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_dependenciesas 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_PATHSproperty of yourpodspec’spod_target_xcconfig:The following changes are automagically applied by Cocoapods
podspecdepends onRCTThirdPartyFabricComponentProvider(Note that it shouldn’t)#import <React/RCTThirdPartyFabricComponentProvider.h>react/renderer/imagemanager/platform/iosfolder:spec.dependency React-ImageManagerto your dependencies${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/imagemanager/platform/iosto yourHEADER_SEARCH_PATHSproperty of your podspec’spod_target_xcconfigspec.dependency React-NativeModulesAppleto your librarypodspecfile.If your library access some files in theReact-RCTFabricpod (any header in theReact/Fabricfolder) add the following line to theHEADER_SEARCH_PATHSproperty of your podspec’spod_target_xcconfig:${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/HeadersFor App Developers that are not using the
RCTAppDelegateclass:RCTAppDelegate, as it will protect you from future changes.RCTAppSetupUtilsfile, you need to update the#importstatements 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/rendererfolder of the framework. The podspec file and the pod name for the C++ code of Fabric is calledReact-FabricReact/Fabricfolder of the repository. The podspec file and the pod name for the iOS specific code of Fabric is calledReact-RCTFabricReact-Codegenand the code is usually inside the<appName>/ios/buildfolder in the final app. Codegen emits also some core components in thereact/renderer/components/rncorefolder and it emits anRCTThirdPartyFabricComponentProviderin 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 thePodfileor to install the dependencies with theNO_FLIPPER=1flag 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
#includestatements. 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-Codegenlibrary, which needs access to some base classes inReact-Fabricin order to generate some valid code (for example, it needs access to the headers contained in thereact/renderer/components/viewnamespace). However, some other components from theReact-Fabricframework needs to access theReact-Codegenframework (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-RCTFabricpod: the podspec for this pod specify anheader_dirofReactand amodule_nameofRCTFabric.The
module_nameis the property used by Cocoapods to actually name the Framework. The name of the framework is part of the#importor#includesyntax, so, whenuse_frameworks!is turned on, to import a file from theReact-RCTFabricframework we need to use the syntax#import <RCTFabric/header.h>. However, currently, all the files that refer to an header in theReact-RCTFabricpod are usingReactas the framework name, as reflected by thepodspec’sheader_dirparameter.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_dirproperty 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_dirare preserved. So, for example, thereact/renderer/graphicsfolder 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/iossubfolder and moved the file from theplatform/iosfolder 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/graphicsfolder 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/iosfolder 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
#includeor#importstatements 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_dependenciesfunction 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_dependenciesfunction 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/HeadersPlus, 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/iosReactCommon/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/iosReactCommon/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/iostextinputReactCommon/react/renderer/textlayoutmanager/. The search path to add is:${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/textinput/iostextinputCocoapods 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
ReactCommonwhich required files fromReact-RCTFabricand thatReact-RCTFabricwas using, and some platform code for Turbomodules, which lives inReactCommonand that requires some files fromReact-Core, whileReact-Coredepends onReactCommon.The circular dependencies introduced by Codegen has been fixed by generating the offending files in some other places. Specifically:
rncorecomponents are now generated inside theReactCommon/react/renderer/components/rncorefolderRCTThirdPartyFabricProvideris now generated directly inside theReact-RCTFabricfolder.There were two circular dependency due to platform specific code: one introduced by
RCTAppSetupUtilsand one by thereact/renderer/ImageManager.The former dependency was caused by
RCTAppSetupUtils.hthat is importing theRTCTurboModuleManager.h. This class lives inReactCommonsoReact-Corewas depending onReactCommon. However, the platform-specific classes in thenativemodulepackage ofReactCommondepends onReact-Corebecause the need to access theRCTBridge. So,ReactCommonwas actually depending onReact-Core. We break that dependency by moving the theRCTAppSetupUtilsto theRCTAppDelegatepod, so now the dependency is broken.To fix the latter, we created a new
podspeccalledReact-ImageManagerthat contains theImageManagerplatform-specific code. This pod depends onReact-Corebut not onReact-RCTFabric, thus the latter can use it freely.Finally, to solve the circular dependency caused by TurboModules, we created a separate
podspecfor platform specific code in theReactCommon/react/nativemodule/platform/ios. By splitting ReactCommon in two separate pods, we can now have thatReactCommondoes not depends on anything,React-Coredepends only onReactCommonand the newreact-NativeModulesAppledepends on both pods.Breaking Changes
If you are an app developer,
RCTAppDelegateclass, this change is not a breaking change at all.#importat the top of yourAppDelegateto reference the new path for theRCTAppSetupUtils.#import <RCTAppSetupUtils.h>If you are a library developer:
RCTThirdPartyFabricProviderfile, you should update the include and import statements with the<React/suffix.imageManager/platform/iosfolder, you should add the new dependency to your podspec file. Specifically, you would need to add:spec.dependency React-ImageManagerspec.dependency 'React-NativeModulesApple' to your librarypodspec` file.We strongly suggest you to migrate to the
RCTAppDelegateand to theinclude_modules_dependenciesfunction 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_PATHproperty of thexcconfig.However, this change prevent anything from actually access those headers naturally. So, we had to update the search paths for the
RCTFabricmodule 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/Fabricfolder. ~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_dependenciesfunction 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 someRCTFabricheader. You should update the search path of your podspec with the proper search paths:${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/HeadersCocoapods should take care of update your search paths automatically.
Beta Was this translation helpful? Give feedback.
All reactions