Skip to content

Commit c3a3614

Browse files
[RN][iOS] Update Module Registration (facebook#4525)
* [RN][iOS] Update Module Registration * Use Cxx instead of C++ Co-authored-by: Nicola Corti <[email protected]> --------- Co-authored-by: Nicola Corti <[email protected]>
1 parent eb71fd5 commit c3a3614

File tree

3 files changed

+124
-31
lines changed

3 files changed

+124
-31
lines changed

docs/the-new-architecture/pure-cxx-modules.md

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -318,46 +318,95 @@ If you did everything right, your project on the left should look like this:
318318

319319
#### 3. Registering the Cxx Turbo Native Module in your app
320320

321-
:::warning
322-
If your app has some local modules that are written in C++, you would not be able to use the AppDelegate in Swift that we shipped in React Native 0.77.
321+
To register a pure Cxx Turbo Native Module in your app, you need to:
323322

324-
If your app falls in this category, please skip the migration of the AppDelegate to Swift, and keep using Objective-C++ for your app's AppDelegate.
323+
1. Create a `ModuleProvider` for the Native Module
324+
2. Configure the `package.json` to associate the JS module name with the ModuleProvider class.
325325

326-
React Native core is mostly developed using C++ to encourage code sharing between iOS and Android and other platforms. The interoperability between Swift and C++ is not mature nor stable, yet. We are looking into ways to fill this gap and let you migrate to Swift too.
327-
:::
326+
The ModuleProvider is an Objective-C++ that glues together the Pure C++ module with the rest of your iOS App.
328327

329-
With this last step, we will tell the iOS app where to look for to find the pure C++ Turbo Native Module.
328+
##### 3.1 Create the ModuleProvider
330329

331-
In Xcode, open the `AppDelegate.mm` file and modify it as follows:
330+
1. From Xcode, select the `SampleApp` project and press <kbd>⌘</kbd> + <kbd>N</kbd> to create a new file.
331+
2. Select the `Cocoa Touch Class` template
332+
3. Add the name `SampleNativeModuleProvider` (keep the other field as `Subclass of: NSObject` and `Language: Objective-C`)
333+
4. Click Next to generate the files.
334+
5. Rename the `SampleNativeModuleProvider.m` to `SampleNativeModuleProvider.mm`. The `mm` extension denotes an Objective-C++ file.
335+
6. Implement the content of the `SampleNativeModuleProvider.h` with the following:
332336

333-
```diff title="SampleApp/AppDelegate.mm"
334-
#import <React/RCTBundleURLProvider.h>
335-
+ #import <RCTAppDelegate+Protected.h>
336-
+ #import "NativeSampleModule.h"
337+
```objc title="NativeSampleModuleProvider.h"
337338

338-
// ...
339-
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
340-
#endif
341-
}
339+
#import <Foundation/Foundation.h>
340+
#import <ReactCommon/RCTTurboModule.h>
342341

343-
+- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
344-
+ jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
345-
+{
346-
+ if (name == "NativeSampleModule") {
347-
+ return std::make_shared<facebook::react::NativeSampleModule>(jsInvoker);
348-
+ }
349-
+
350-
+ return [super getTurboModule:name jsInvoker:jsInvoker];
351-
+}
342+
NS_ASSUME_NONNULL_BEGIN
343+
344+
@interface NativeSampleModuleProvider : NSObject <RCTModuleProvider>
352345

353346
@end
347+
348+
NS_ASSUME_NONNULL_END
354349
```
355350

356-
These changes are doing a few things:
351+
This declares a `NativeSampleModuleProvider` object that conforms to the `RCTModuleProvider` protocol.
357352

358-
1. Importing the `RCTAppDelegate+Protected` header to make it visible to the AppDelegate that it is conforming to the `RCTTurboModuleManagerDelegate` protocol.
359-
2. Importing the Pure C++ Native Turbo Module interface `NativeSampleModule.h`
360-
3. Overriding the `getTurboModule` method for C++ modules so that when the JS side asks for a module called `NativeSampleModule`, the app knows which module has to be returned.
353+
7. Implement the content of the `SampleNativeModuleProvider.mm` with the following:
354+
355+
```objc title="NativeSampleModuleProvider.mm"
356+
357+
#import "NativeSampleModuleProvider.h"
358+
#import <ReactCommon/CallInvoker.h>
359+
#import <ReactCommon/TurboModule.h>
360+
#import "NativeSampleModule.h"
361+
362+
@implementation NativeSampleModuleProvider
363+
364+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
365+
(const facebook::react::ObjCTurboModule::InitParams &)params
366+
{
367+
return std::make_shared<facebook::react::NativeSampleModule>(params.jsInvoker);
368+
}
369+
370+
@end
371+
```
372+
373+
This code implements the `RCTModuleProvider` protocol by creating the pure C++ `NativeSampleModule` when the `getTurboModule:` method is called.
374+
375+
##### 3.2 Update the package.json
376+
377+
The last step consist in updating the `package.json` to tell React Native about the link between the JS specs of the Native Module and the concrete implementation of those spec in native code.
378+
379+
Modify the `package.json` as it follows:
380+
381+
```json title="package.json"
382+
"start": "react-native start",
383+
"test": "jest"
384+
},
385+
"codegenConfig": {
386+
"name": "AppSpecs",
387+
"type": "modules",
388+
"jsSrcsDir": "specs",
389+
"android": {
390+
"javaPackageName": "com.sampleapp.specs"
391+
}
392+
// highlight-add-start
393+
"ios":
394+
"modulesProvider": {
395+
"NativeSampleModule": "NativeSampleModuleProvider"
396+
}
397+
// highlight-add-end
398+
},
399+
400+
"dependencies": {
401+
```
402+
403+
At this point, you need to re-install the pods to make sure that codegen runs again to generate the new files:
404+
405+
```bash
406+
# from the ios folder
407+
bundle exec pod install
408+
open SampleApp.xcworkspace
409+
```
361410

362411
If you now build your application from Xcode, you should be able to build successfully.
363412

docs/the-new-architecture/using-codegen.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ npx @react-native-community/cli@latest init SampleApp --version 0.76.0
5555
"componentProvider": {
5656
"<componentName>": "<iOS-class-implementing-the-component>"
5757
},
58+
"modulesProvider": {
59+
"<moduleName>": "<iOS-class-implementing-the-RCTModuleProvider-protocol>"
60+
}
5861
}
5962
},
6063
```
@@ -74,6 +77,7 @@ You can add this snippet to your app and customize the various fields:
7477
- `ios.modulesConformingToProtocol.RCTURLRequestHandler`: list of iOS native module that implements the [`RCTURLRequestHandler` protocol](https://github.com/facebook/react-native/blob/00d5caee9921b6c10be8f7d5b3903c6afe8dbefa/packages/react-native/React/Base/RCTURLRequestHandler.h#L11-L52). You need to pass the class names of iOS classes that implements the `RCTURLRequestHandler`. They must be Native Modules.
7578
- `ios.modulesConformingToProtocol.RCTImageDataDecoder`: list of iOS native module that implements the [`RCTImageDataDecoder` protocol](https://github.com/facebook/react-native/blob/00d5caee9921b6c10be8f7d5b3903c6afe8dbefa/packages/react-native/Libraries/Image/RCTImageDataDecoder.h#L15-L53). You need to pass the class names of iOS classes that implements the `RCTImageDataDecoder`. They must be Native Modules.
7679
- `ios.componentProvider`: this field is a map used to generate the association between a custom JS React component and the native class that implements it. The key of the map is the JS name of the component (for example `TextInput`), and the value is the iOS class that implements the component (for example `RCTTextInput`).
80+
- `ios.modulesProvider`: this field is a map used to generate the association between a custom JS Native Module and the native class that can provide it. The key of the map is the JS name of the module (for example `NativeLocalStorage`), and the value is the iOS class that implements the [`RCTModuleProvider` protocol](https://github.com/facebook/react-native/blob/0.79-stable/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h#L179-L190). For Objective-C modules, the class implementing the [`RCTTurboModule` protocol](https://github.com/facebook/react-native/blob/0.79-stable/packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h#L192-L200) is also implementing the `RCTModuleProvider` protocol. For more information, looks at the [Cross-Platform Native Modules (C++) guide](/docs/next/the-new-architecture/pure-cxx-modules).
7781

7882
When **Codegen** runs, it searches among all the dependencies of the app, looking for JS files that respects some specific conventions, and it generates the required code:
7983

docs/turbo-native-modules-ios.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ static NSString *const RCTNativeLocalStorageKey = @"local-storage";
7878

7979
@implementation RCTNativeLocalStorage
8080

81-
RCT_EXPORT_MODULE(NativeLocalStorage)
82-
8381
- (id) init {
8482
if (self = [super init]) {
8583
_localStorage = [[NSUserDefaults alloc] initWithSuiteName:RCTNativeLocalStorageKey];
@@ -111,14 +109,56 @@ RCT_EXPORT_MODULE(NativeLocalStorage)
111109
}
112110
}
113111

112+
+ (NSString *)moduleName
113+
{
114+
return @"NativeLocalStorage";
115+
}
116+
114117
@end
115118
```
116119
117120
Important things to note:
118121
119-
- `RCT_EXPORT_MODULE` exports and registers the module using the identifier we'll use to access it in the JavaScript environment: `NativeLocalStorage`. See these [docs](./legacy/native-modules-ios#module-name) for more details.
120122
- You can use Xcode to jump to the Codegen `@protocol NativeLocalStorageSpec`. You can also use Xcode to generate stubs for you.
121123
124+
## Register the Native Module in your app
125+
126+
The last step consist in updating the `package.json` to tell React Native about the link between the JS specs of the Native Module and the concrete implementation of those specs in native code.
127+
128+
Modify the `package.json` as it follows:
129+
130+
```json title="package.json"
131+
"start": "react-native start",
132+
"test": "jest"
133+
},
134+
"codegenConfig": {
135+
"name": "AppSpecs",
136+
"type": "modules",
137+
"jsSrcsDir": "specs",
138+
"android": {
139+
"javaPackageName": "com.sampleapp.specs"
140+
}
141+
// highlight-add-start
142+
"ios":
143+
"modulesProvider": {
144+
"NativeLocalStorage": "RCTNativeLocalStorage"
145+
}
146+
// highlight-add-end
147+
},
148+
149+
"dependencies": {
150+
```
151+
152+
At this point, you need to re-install the pods to make sure that codegen runs again to generate the new files:
153+
154+
```bash
155+
# from the ios folder
156+
bundle exec pod install
157+
open SampleApp.xcworkspace
158+
```
159+
160+
If you now build your application from Xcode, you should be able to build successfully.
161+
122162
## Build and run your code on a Simulator
123163

124164
<Tabs groupId="package-manager" queryString defaultValue={constants.defaultPackageManager} values={constants.packageManagers}>

0 commit comments

Comments
 (0)