From f7cadd1dda46c57637822fc4b4f5b84b2081e17f Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Wed, 17 Aug 2022 10:58:50 +0100 Subject: [PATCH] [New Architecture][iOS][0.71.0] Update the App Migration section --- docs/hermes.md | 12 ++ docs/new-architecture-app-intro.md | 167 ++++++++--------- docs/new-architecture-app-modules-ios.md | 173 ------------------ docs/new-architecture-app-renderer-ios.md | 93 ---------- docs/new-architecture-intro.md | 2 - docs/react-18-and-react-native.md | 1 - .../pillars-turbomodule.md | 2 +- docs/the-new-architecture/use-app-template.md | 3 +- website/sidebars.json | 4 +- 9 files changed, 88 insertions(+), 369 deletions(-) delete mode 100644 docs/new-architecture-app-modules-ios.md delete mode 100644 docs/new-architecture-app-renderer-ios.md diff --git a/docs/hermes.md b/docs/hermes.md index cd6b6c9eaf4..14a7bfa7150 100644 --- a/docs/hermes.md +++ b/docs/hermes.md @@ -36,6 +36,18 @@ Edit your `android/app/build.gradle` file and make the change illustrated below: - enableHermes: false // clean and rebuild if changing + enableHermes: true // clean and rebuild if changing ] + +// ... + +if (enableHermes) { +- def hermesPath = "../../node_modules/hermes-engine/android/"; +- debugImplementation files(hermesPath + "hermes-debug.aar") +- releaseImplementation files(hermesPath + "hermes-release.aar") ++ //noinspection GradleDynamicVersion ++ implementation("com.facebook.react:hermes-engine:+") { // From node_modules ++ exclude group:'com.facebook.fbjni' ++ } +} else { ``` Also, if you're using ProGuard, you will need to add these rules in `proguard-rules.pro` : diff --git a/docs/new-architecture-app-intro.md b/docs/new-architecture-app-intro.md index dc7948d4dff..dc3d055a82f 100644 --- a/docs/new-architecture-app-intro.md +++ b/docs/new-architecture-app-intro.md @@ -13,21 +13,29 @@ There are a few prerequisites that should be addressed before the New Architectu React Native released the support for the New Architecture with the release `0.68.0`. -This guide is written with the expectation that you’re using the latest React Native release. At the moment of writing, this is `0.70.0`. Other than this guide, you can leverage the [upgrade helper](https://react-native-community.github.io/upgrade-helper/) to determine what other changes may be required for your project. +This guide is written with the expectation that you’re using the latest React Native release. At the moment of writing, this is `0.71.0`. Other than this guide, you can leverage the [upgrade helper](https://react-native-community.github.io/upgrade-helper/) to determine what other changes may be required for your project. To update to the most recent version of React Native, you can run this command: ```bash -yarn add react-native@0.70.0 +npx react-native upgrade ``` -Starting from React Native `0.69.0`, you may also need to update the version of React to 18. You can do so by using this command: +## Use Hermes -```bash -yarn add react@18.0.0 -``` +Hermes is an open-source JavaScript engine optimized for React Native. Hermes is enabled by default and you have to explicitly disable it if you want to use JSC. + +We highly recommend using Hermes in your application. With Hermes enabled, you will be able to use the JavaScript debugger in Flipper to directly debug your JavaScript code. + +Please [follow the instructions on the React Native website](hermes) to learn how to enable/disable Hermes. + +:::caution + +**iOS:** If you opt out of using Hermes, you will need to replace `HermesExecutorFactory` with `JSCExecutorFactory` in any examples used throughout the rest of this guide. + +::: -### Android specifics +## Android - Update Build System Using the New Architecture on Android has some prerequisites that you need to meet: @@ -149,63 +157,27 @@ dependencies { + implementation project(":ReactAndroid") // From node_modules ``` -## Use Hermes - -Hermes is an open-source JavaScript engine optimized for React Native. Hermes is enabled by default and you have to explicitly disable it if you want to use JSC. +## iOS - Make the project build -We highly recommend using Hermes in your application. With Hermes enabled, you will be able to use the JavaScript debugger in Flipper to directly debug your JavaScript code. - -Please [follow the instructions on the React Native website](hermes) to learn how to enable/disable Hermes. - -:::caution - -**iOS:** If you opt out of using Hermes, you will need to replace `HermesExecutorFactory` with `JSCExecutorFactory` in any examples used throughout the rest of this guide. - -::: - -### Android +After upgrading the project, there are a few changes you need to apply: -To enable Hermes in Android, open the `android/app/build.gradle` and apply the following changes: +1. Target the proper iOS version. Open the `Podfile` and apply this change: ```diff -project.ext.react = [ -- enableHermes: true, // clean and rebuild if changing -+ enableHermes: true, // clean and rebuild if changing -] -// ... - -} - -if (enableHermes) { -- def hermesPath = "../../node_modules/hermes-engine/android/"; -- debugImplementation files(hermesPath + "hermes-debug.aar") -- releaseImplementation files(hermesPath + "hermes-release.aar") -+ //noinspection GradleDynamicVersion -+ implementation("com.facebook.react:hermes-engine:+") { // From node_modules -+ exclude group:'com.facebook.fbjni' -+ } -} else { -``` - -Moreover, you'll need to update the `proguard-rules`, adding the following ones: - -``` --keep class com.facebook.hermes.unicode.** { *; } --keep class com.facebook.jni.** { *; } +- platform :ios, '11.0' ++ platform :ios, '12.4' ``` -After that, remember to cleanup the project, running +2. Create an `.xcode.env` file to export the locaion of the NODE_BINARY. Navigate to the `ios` folder and run this command: ```sh -cd android -./gradlew clean +echo 'export NODE_BINARY=$(command -v node)' > .xcode.env ``` -## iOS: Make the project build - -After upgrading the project, there are a few changes you need to apply: +If you need it, you can also open the file and replace the `$(command -v node)` with the path to the node executable. +React Native supports also a local version of this file `.xcode.env.local`. This file is not synced with the repository to let you customize your local setup, if it differs from the Continuous Integration or the team one. -1. Fix an API change in the `AppDelegate.m`. Open this file and apply this change: +2. Fix an API change in the `AppDelegate.m`. Open this file and apply this change: ```diff #if DEBUG @@ -214,67 +186,74 @@ After upgrading the project, there are a few changes you need to apply: #else ``` -2. Target the proper iOS version. Open the `Podfile` and apply this change: +## iOS - Use Objective-C++ (`.mm` extension) -```diff -- platform :ios, '11.0' -+ platform :ios, '12.4' -``` +TurboModules can be written using Objective-C or C++. In order to support both cases, any source files that include C++ code should use the `.mm` file extension. This extension corresponds to Objective-C++, a language variant that allows for the use of a combination of C++ and Objective-C in source files. -3. Create an `.xcode.env` file to export the locaion of the NODE_BINARY. Navigate to the `ios` folder and run this command: +:::important -```sh -echo 'export NODE_BINARY=$(command -v node)' > .xcode.env -``` +**Use Xcode to rename existing files** to ensure file references persist in your project. You might need to clean the build folder (_Project → Clean Build Folder_) before re-building the app. If the file is renamed outside of Xcode, you may need to click on the old `.m` file reference and Locate the new file. -If you need it, you can also open the file and replace the `$(command -v node)` with the path to the node executable. -React Native supports also a local version of this file `.xcode.env.local`. This file is not synced with the repository to let you customize your local setup, if it differs from the Continuous Integration or the team one. +::: -## iOS: Use Objective-C++ (`.mm` extension) +## iOS - Make your AppDelegate conform to `RCTAppDelegate` -TurboModules can be written using Objective-C or C++. In order to support both cases, any source files that include C++ code should use the `.mm` file extension. This extension corresponds to Objective-C++, a language variant that allows for the use of a combination of C++ and Objective-C in source files. +The final step to configure iOS for the New Architecture is to extend a base class proided by React Native, called `RCTAppDelegate`. -:::info +This class provides a base implementation for all the required functionalities of the new architecture. If you need to customize some of them, you can override those methods, invoke `[super methodNameWith:parameters:];` collecting the returned value and customize the bits you need to customize. -Use Xcode to rename existing files to ensure file references persist in your project. You might need to clean the build folder (_Project → Clean Build Folder_) before re-building the app. If the file is renamed outside of Xcode, you may need to click on the old `.m` file reference and Locate the new file. +1. Open the `ios/AppDelegate.h` file and update it as it follows: -::: - -## iOS: TurboModules: Ensure your App Provides an `RCTCxxBridgeDelegate` +```diff +- #import ++ #import +#import -In order to set up the TurboModule system, you will add some code to interact with the bridge in your AppDelegate. Before you start, go ahead and rename your AppDelegate file to use the `.mm` extension. +- @interface AppDelegate : UIResponder ++ @interface AppDelegate : RCTAppDelegate -Now you will have your AppDelegate conform to `RCTCxxBridgeDelegate`. Start by adding the following imports at the top of your AppDelegate file: +- @property (nonatomic, strong) UIWindow *window; -```objc -#import -#import -#import +@end ``` -Then, declare your app delegate as a `RCTCxxBridgeDelegate` provider: +2. Open the `ios/AppDelegate.mm` file and replace its content with the following: ```objc -@interface AppDelegate () { - // ... +#import "AppDelegate.h" +#import + +@implementation AppDelegate + - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.moduleName = @"NameOfTheApp"; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; +#else + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; +#endif } + +- (BOOL)concurrentRootEnabled +{ + return true; +} + @end ``` -To conform to the `RCTCxxBridgeDelegate` protocol, you will need to implement the `jsExecutorFactoryForBridge:` method. Typically, this is where you would return a `JSCExecutorFactory` or `HermesExecutorFactory`, and we will use it to install our TurboModules bindings later on. - -You can implement the `jsExecutorFactoryForBridge:` method like this: +:::note +The `moduleName` has to be the same string used in the `[RCTRootView initWithBridge:moduleName:initialProperties]` call in the original `AppDelegate.mm` file. +::: -```objc -#pragma mark - RCTCxxBridgeDelegate +## iOS - Run pod install -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge -{ - return std::make_unique(facebook::react::RCTJSIExecutorRuntimeInstaller([bridge](facebook::jsi::Runtime &runtime) { - if (!bridge) { - return; - } - }) - ); -} +```bash +// Run pod install with the flags +RCT_NEW_ARCH_ENABLED=1 pod install ``` diff --git a/docs/new-architecture-app-modules-ios.md b/docs/new-architecture-app-modules-ios.md deleted file mode 100644 index ca6f9306a1c..00000000000 --- a/docs/new-architecture-app-modules-ios.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -id: new-architecture-app-modules-ios -title: Enabling TurboModule on iOS ---- - -import NewArchitectureWarning from './\_markdown-new-architecture-warning.mdx'; - - - -Make sure your application meets all the [prerequisites](new-architecture-app-intro). - -## 1. Provide a TurboModuleManager Delegate - -Add the following imports at the top of your bridge delegate (e.g. `AppDelegate.mm`): - -```objc -#import -#import -``` - -You will also need to declare that your AppDelegate conforms to the `RCTTurboModuleManagerDelegate` protocol, as well as create an instance variable for our Turbo Module manager: - -```objc -@interface AppDelegate () { - // ... - RCTTurboModuleManager *_turboModuleManager; -} -@end -``` - -To conform to the `RCTTurboModuleManagerDelegate` protocol, you will implement these three methods: - -- `getModuleClassFromName:` - This method should return the Class for a native module. You may use the `RCTCoreModulesClassProvider()` method to handle the default, core modules. -- `getTurboModule:jsInvoker:` - This should return `nullptr`. This method may be used later to support C++ TurboModules. -- `getModuleInstanceFromClass:moduleClass:` - This method allows you to perform any side-effects when your TurboModules are initialized. This is the TurboModule analogue to your bridge delegate’s `extraModulesForBridge` method. At this time, you’ll need to initialize the default RCTNetworking and RCTImageLoader modules as indicated below. - -#### TurboModuleManagerDelegate Example - -Take note of `getModuleInstanceFromClass:` in the following example, as it includes some necessary instantiation of several core modules that you will need to include in your application. Eventually, this may not be required. - -```objc title='AppDelegate.mm' -// ... - -#import -#import -#import -#import -#import -#import -#import - -#import - -#import - -// ... - -#pragma mark RCTTurboModuleManagerDelegate - -- (Class)getModuleClassFromName:(const char *)name -{ - return RCTCoreModulesClassProvider(name); -} - -- (std::shared_ptr) - getTurboModule:(const std::string &)name - jsInvoker:(std::shared_ptr)jsInvoker { - return nullptr; -} - -- (id)getModuleInstanceFromClass:(Class)moduleClass -{ - // Set up the default RCTImageLoader and RCTNetworking modules. - if (moduleClass == RCTImageLoader.class) { - return [[moduleClass alloc] initWithRedirectDelegate:nil - loadersProvider:^NSArray> *(RCTModuleRegistry * moduleRegistry) { - return @ [[RCTLocalAssetImageLoader new]]; - } - decodersProvider:^NSArray> *(RCTModuleRegistry * moduleRegistry) { - return @ [[RCTGIFImageDecoder new]]; - }]; - } else if (moduleClass == RCTNetworking.class) { - return [[moduleClass alloc] - initWithHandlersProvider:^NSArray> *( - RCTModuleRegistry *moduleRegistry) { - return @[ - [RCTHTTPRequestHandler new], - [RCTDataRequestHandler new], - [RCTFileRequestHandler new], - ]; - }]; - } - // No custom initializer here. - return [moduleClass new]; -} -``` - -## 2. Install TurboModuleManager JavaScript Bindings - -Next, you will create a `RCTTurboModuleManager` in your bridge delegate’s `jsExecutorFactoryForBridge:` method, and install the JavaScript bindings: - -```objc -#pragma mark - RCTCxxBridgeDelegate - -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge -{ - // Add these lines to create a TurboModuleManager - if (RCTTurboModuleEnabled()) { - _turboModuleManager = - [[RCTTurboModuleManager alloc] initWithBridge:bridge - delegate:self - jsInvoker:bridge.jsCallInvoker]; - - // Necessary to allow NativeModules to lookup TurboModules - [bridge setRCTTurboModuleRegistry:_turboModuleManager]; - - if (!RCTTurboModuleEagerInitEnabled()) { - /** - * Instantiating DevMenu has the side-effect of registering - * shortcuts for CMD + d, CMD + i, and CMD + n via RCTDevMenu. - * Therefore, when TurboModules are enabled, we must manually create this - * NativeModule. - */ - [_turboModuleManager moduleForName:"DevMenu"]; - } - } - - // Add this line... - __weak __typeof(self) weakSelf = self; - - // If you want to use the `JSCExecutorFactory`, remember to add the `#import ` - // import statement on top. - return std::make_unique( - facebook::react::RCTJSIExecutorRuntimeInstaller([weakSelf, bridge](facebook::jsi::Runtime &runtime) { - if (!bridge) { - return; - } - - // And add these lines to install the bindings... - __typeof(self) strongSelf = weakSelf; - if (strongSelf) { - facebook::react::RuntimeExecutor syncRuntimeExecutor = - [&](std::function &&callback) { callback(runtime); }; - [strongSelf->_turboModuleManager installJSBindingWithRuntimeExecutor:syncRuntimeExecutor]; - } - })); -} -``` - -## 3. Enable TurboModule System - -Finally, enable TurboModules in your app by executing the following statement before React Native is initialized in your app delegate (e.g. within `didFinishLaunchingWithOptions:`): - -```objc -RCTEnableTurboModule(YES); -``` - -#### Example - -```objc -@implementation AppDelegate -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - RCTEnableTurboModule(YES); - - RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self - launchOptions:launchOptions]; - - // ... - - return YES; -} -``` diff --git a/docs/new-architecture-app-renderer-ios.md b/docs/new-architecture-app-renderer-ios.md deleted file mode 100644 index 39f3226f55f..00000000000 --- a/docs/new-architecture-app-renderer-ios.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -id: new-architecture-app-renderer-ios -title: Enabling Fabric on iOS ---- - -import M1Cocoapods from './\_markdown-m1-cocoapods.mdx'; -import NewArchitectureWarning from './\_markdown-new-architecture-warning.mdx'; - - - -This section will go over how to enable the new renderer in your app. Make sure your application meets all the [prerequisites](new-architecture-app-intro). - -## 1. Enable Fabric in Podfile - -Add changes to your Podfile. You can see some examples in [RNTester](https://github.com/facebook/react-native/blob/main/packages/rn-tester/Podfile) and [rn-demo-app](https://github.com/facebook/fbt/blob/rn-demo-app/ios/Podfile). - -```ruby title="Podfile" -# Add the following line at the top of Podfile. -# Codegen produces files/classes that share names, and it will show the warning. -# deterministic_uuids option surpresses the warning. -install! 'cocoapods', :deterministic_uuids => false -target 'Some App' do - pods() -end -def pods() - # Get config - config = use_native_modules! - # Use env variables to turn it on/off. - fabric_enabled = ENV['USE_FABRIC'] - use_react_native!( - ... - # Modify here if your app root path isn't the same as this one. - :app_path => "#{Dir.pwd}/..", - # Pass the flag to enable fabric to use_react_native!. - :fabric_enabled => fabric_enabled - ) -end -``` - -## 2. Update your root view - -How to render your app with Fabric depends on your setup. Here is an example of how you can enable Fabric in your app with the `RN_FABRIC_ENABLED` compiler flag to enable/disable. Refer [RN-Tester’s AppDelegate](https://github.com/facebook/react-native/blob/main/packages/rn-tester/RNTester/AppDelegate.mm) as an example. - -```objc title="AppDelegate.mm" -#ifdef RN_FABRIC_ENABLED -#import -#import -#import -#import -#endif - -@interface AppDelegate () { -#ifdef RN_FABRIC_ENABLED - RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; - std::shared_ptr _reactNativeConfig; - facebook::react::ContextContainer::Shared _contextContainer; -#endif - - // Find a line that define rootView and replace/edit with the following lines. - -#ifdef RN_FABRIC_ENABLED - _contextContainer = std::make_shared(); - _reactNativeConfig = std::make_shared(); - - _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); - - _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] - initWithBridge:bridge - contextContainer:_contextContainer]; - - bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; - - UIView *rootView = - [[RCTFabricSurfaceHostingProxyRootView alloc] initWithBridge:bridge - moduleName:<#moduleName#> - initialProperties:@{}]; -#else - // Current implementation to define rootview. - RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge - moduleName:<#moduleName#> - initialProperties:@{}]; -#endif -``` - -## 3. Run pod install - -```bash -// Run pod install with the flags -USE_FABRIC=1 RCT_NEW_ARCH_ENABLED=1 pod install -``` - - diff --git a/docs/new-architecture-intro.md b/docs/new-architecture-intro.md index fa88aeb9e8d..68e44846a56 100644 --- a/docs/new-architecture-intro.md +++ b/docs/new-architecture-intro.md @@ -24,10 +24,8 @@ The guide is divided into five sections: - [Prerequisites for Supporting the New Architecture in your App](new-architecture-app-intro) - Enabling the New NativeModule System (TurboModule) in your App - [Android](new-architecture-app-modules-android) - - [iOS](new-architecture-app-modules-ios) - Enabling the New Renderer (Fabric) in your App - [Android](new-architecture-app-renderer-android) - - [iOS](new-architecture-app-renderer-ios) - [**React 18 & React Native**](react-18-and-react-native) - [**Troubleshooting**](new-architecture-troubleshooting) - [**Appendix**](new-architecture-appendix) diff --git a/docs/react-18-and-react-native.md b/docs/react-18-and-react-native.md index ed4a5d09eb6..acbe7874440 100644 --- a/docs/react-18-and-react-native.md +++ b/docs/react-18-and-react-native.md @@ -110,7 +110,6 @@ On iOS, you'll have access to the `concurrentRootEnabled` method on your `AppDel /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. - (BOOL)concurrentRootEnabled { - // Switch this bool to turn on and off the concurrent root return true; } ``` diff --git a/docs/the-new-architecture/pillars-turbomodule.md b/docs/the-new-architecture/pillars-turbomodule.md index 9ccd7a9820f..ea1d0576020 100644 --- a/docs/the-new-architecture/pillars-turbomodule.md +++ b/docs/the-new-architecture/pillars-turbomodule.md @@ -17,7 +17,7 @@ TurboModules are the next iteration on Native Modules that provide a few extra [ - Lazy loading of modules, allowing for faster app startup - The use of JSI, a JavaScript interface for native code, which allows for more efficient communication between native and JavaScript code than the bridge -This guide will show you how to create a basic TurboModule. +This guide will show you how to create a basic TurboModule compatible with React Native 0.70.0. :::caution TurboModules only work with the **New Architecture** enabled. diff --git a/docs/the-new-architecture/use-app-template.md b/docs/the-new-architecture/use-app-template.md index ca4ed3d3dad..f691208d86b 100644 --- a/docs/the-new-architecture/use-app-template.md +++ b/docs/the-new-architecture/use-app-template.md @@ -128,5 +128,4 @@ For further explanations of what each file is doing, check out these guides to w #### iOS -- [Enabling TurboModules on iOS](new-architecture-app-modules-ios.md) -- [Enabling Fabric on iOS](new-architecture-app-renderer-ios.md) +- [Enabling The New Architecture in Your App](new-architecture-app-intro.md) diff --git a/website/sidebars.json b/website/sidebars.json index 56db20c5502..504f2d8947a 100644 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -130,9 +130,7 @@ "items": [ "new-architecture-app-intro", "new-architecture-app-modules-android", - "new-architecture-app-modules-ios", - "new-architecture-app-renderer-android", - "new-architecture-app-renderer-ios" + "new-architecture-app-renderer-android" ] }, "react-18-and-react-native",