The TurboModule system is nearly feature-complete on both Android and iOS. Before we rollout the system, we'd like to migrate all our OSS NativeModules to use it. As a prerequisite for this migration, all the the NativeModules must have spec files. There are currently 41 NativeModules in total, so there is a bunch of work to do. We'd love to use this as an opportunity to help get more people involved by contributing to React Native.
NativeXYZ.js
, where XYZ
is the name of the NativeModule.NativeModules.XYZ
to NativeXYZ
A Spec file looks like this:
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @flow strict-local
* @format
*/
'use strict';
import type {TurboModule} from 'RCTExport';
import * as TurboModuleRegistry from 'TurboModuleRegistry';
export interface Spec extends TurboModule {
+getConstants: () => {|
constant1: string,
constant2: boolean,
|};
+logCounter: (key: string, value: number) => void;
+logEvent: (eventName: string, data: Object, analyticsModule: ?string) => void;
+logRealtimeEvent: (
eventName: string,
data: Object,
analyticsModule: ?string,
) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'Analytics',
);
TurboModuleRegistry.getEnforcing<Spec>('Analytics')
as opposed to NativeModules.Analytics
to require the NativeModule. TurboModuleRegistry.getEnforcing
is an indirection that allows us to require both NativeModules and TurboModules. It throws if the NativeModule isn't there. In the case that a NativeModule is platform-specific, please use Platform.OS and conditionally call TurboModuleRegistry.getEnforcing
.interface
called Spec
that extends TurboModule
. This interface contains typings for methods exposed by the NativeModule/TurboModule.getConstants()
method. The old way of accessing constants as properties on NativeModule objects is deprecated and unsupported by the TurboModule system. On iOS, these constants map to the return type of constantsToExport
in iOS and getConstants
in Android.Native*.js
. The filename matters because our codegen uses it to name the generated Java interfaces, ObjC protocols, and C++ TurboModule subclasses. NativeAnalytics.js
, for instance, will generate a Java interface and ObjC protocol called NativeAnalyticsSpec
, and a C++ TurboModule class called NativeAnalyticsSpecJSI
. After these Spec files are typed, we'll generate and check in the codegen output into this repository. Then, after we open source our codegen, we'll delete the generated interfaces, protocols, and C++ classes from this repository.string
boolean
number
Object
Array<*>*
Function
and typed function:?string
?Object
?Array<*>*
?Function
and nullable typed functionnumber
and boolean
are not supported(x: {|foo: string, ... |}) => void
Promise<*>*
string
number
boolean
Object
Array<*>*
You have to deduce the types by looking at the NativeModule implementations on iOS and Android. Some NativeModules already have JS wrappers, so you could also look at those for writing the Spec files.
How do you know which methods are exported to JS?
@ReactMethod
.RCT_EXPORT_*_METHOD
macros . How do you know when a method should have a Promise
return type?
Promise
object.RCTPromiseResolveBlock
and RCTPromiseRejectBlock
What should the JS method name be?
NSDictionary
on iOS, and ReadableMap
and WritableMap
on Android translate to object literals in JS.RCTResponseSenderBlock
and RCTResponseErrorBlock
on iOS, and Callback
on Android translate to function literals in JS.Function
and Object
whenever possible. react-native-github/Libraries/NativeModules/specs
.For platform-specific NativeModules, use Platform.OS
to conditionally call TurboModuleRegistry.getEnforcing<Spec>
. When changing the call-sites of these NativeModules, you may have to guard them with null checks to please flow.
const NativeModules = require('NativeModules');
const IntentAndroid = NativeModules.IntentAndroid;
function foo() {
if (Platform.OS === 'android') {
IntentAndroid.method();
}
}
const NativeIntentAndroid = require('NativeIntentAndroid');
const {Platform} = require('react-native');
function foo() {
if (Platform.OS === 'android') {
if (NativeIntentAndroid != null) {
NativeIntentAndroid.method();
}
}
}
Please claim a NativeModule from the list below.
β¬οΈ - Unclaimed
π§ - Claimed / PR in progress
β
- PR Merged
|Status |JS Name |JS NativeModule Wrappers |iOS |Android |
|--- |--- |--- |--- |--- |
| β
|AccessibilityInfo |react-native-github/Libraries/Components/AccessibilityInfo/AccessibilityInfo.android.js | |AccessibilityInfoModule.java |
| β
|AccessibilityManager |react-native-github/Libraries/Components/AccessibilityInfo/AccessibilityInfo.ios.js |RCTAccessibilityManager.m | |
| β
|AlertManager |react-native-github/Libraries/Alert/Alert.js |RCTAlertManager.m | |
| β
|AnimationDebugModule | | |AnimationsDebugModule.java |
| β
|AppState |react-native-github/Libraries/AppState/AppState.js |RCTAppState.m |AppStateModule.java |
| β
|BlobModule |react-native-github/Libraries/Blob/BlobManager.js |RCTBlobManager.mm |BlobModule.java |
| β
|DatePickerAndroid |react-native-github/Libraries/Components/DatePickerAndroid/DatePickerAndroid.android.js | |DatePickerDialogModule.java |
| β
|DevLoadingView |react-native-github/Libraries/Utilities/HMRLoadingView.ios.js |RCTDevLoadingView.m | |
| β
|DevSettings |N/A |RCTDevSettings.mm |DevSettingsModule.java |
| β
|DeviceEventManager |N/A | |DeviceEventManagerModule.java |
| β
|DeviceInfo |react-native-github/Libraries/Utilities/DeviceInfo.js |RCTDeviceInfo.m |DeviceInfoModule.java |
| β
|DialogManagerAndroid |N/A | |DialogModule.java |
| β
|ExceptionsManager |react-native-github/Libraries/Core/ExceptionsManager.js |RCTExceptionsManager.m |ExceptionsManagerModule.java |
| β
|FileReaderModule |N/A |RCTFileReaderModule.m |FileReaderModule.java |
| β
|HeadlessJsTaskSupport |N/A | |HeadlessJsTaskSupportModule.java |
| β
|I18nManager |react-native-github/Libraries/ReactNative/I18nManager.js |RCTI18nManager.m |I18nManagerModule.java |
| β
|ImageEditor |N/A |RCTImageEditingManager.m |ImageEditingModule.java |
| β
|ImageStore |N/A |RCTImageStoreManager.m | ImageStoreManager.java |
| β
|IntentAndroid |react-native-github/Libraries/Linking/Linking.js | |IntentModule.java |
| β
|JSCHeapCapture |react-native-github/Libraries/Utilities/HeapCapture.js | |JSCHeapCapture.java |
| β
|JSCSamplingProfiler |react-native-github/Libraries/Performance/SamplingProfiler.js | |JSCSamplingProfiler.java |
| β
|JSDevSupport |react-native-github/Libraries/Utilities/JSDevSupportModule.js | |JSDevSupport.java |
| β
|KeyboardObserver |react-native-github/Libraries/Components/Keyboard/Keyboard.js (NativeEventEmitter) |RCTKeyboardObserver.m | |
| β
|LinkingManager |react-native-github/Libraries/Linking/Linking.js |RCTLinkingManager.m | |
| β
|ModalManager |N/A |RCTModalManager.m | |
| β
|NativeAnimatedModule |react-native-github/Libraries/Animated/src/NativeAnimatedHelper.js |RCTNativeAnimatedModule.m |NativeAnimatedModule.java |
| β
|Networking |react-native-github/Libraries/Network/RCTNetworking.android.js |RCTNetworking.mm |NetworkingModule.java |
| β
|react-native-github/Libraries/Network/RCTNetworking.ios.js |
| β
|PermissionsAndroid |react-native-github/Libraries/PermissionsAndroid/PermissionsAndroid.js | |PermissionsModule.java |
| β
|PlatformConstants |react-native-github/Libraries/Utilities/Platform.android.js |RCTPlatform.m |AndroidInfoModule.java |
| β
|react-native-github/Libraries/Utilities/Platform.ios.js |
| β
|RedBox |N/A |RCTRedBox.m | |
| β
|SettingsManager |react-native-github/Libraries/Settings/Settings.ios.js |RCTSettingsManager | |
| β
|SourceCode |react-native-github/Libraries/Share/Share.js |RCTSourceCode.m |SourceCodeModule.java |
| β
|TVNavigationEventEmitter |react-native-github/Libraries/Components/AppleTV/TVEventHandler.js |RCTTVNavigationEventEmitter.m | |
| β
|TimePickerAndroid |react-native-github/Libraries/Components/TimePickerAndroid/TimePickerAndroid.android.js | |TimePickerDialogModule.java |
| β
|react-native-github/Libraries/Components/TimePickerAndroid/TimePickerAndroid.ios.js |
| β
|Timing |react-native-github/Libraries/Core/Timers/JSTimers.js |RCTTiming.m |Timing.java |
| β
|ToastAndroid |react-native-github/Libraries/Components/ToastAndroid/ToastAndroid.android.js | |ToastModule.java |
| β
|UIManager | |RCTUIManager.m |UIManagerModule.java |
| β
|WebSocketModule |react-native-github/Libraries/WebSocket/WebSocket.js |RCTWebSocketModule.m |WebSocketModule.java |
@RSNara any examples on how to handle event emitter classes?
any examples on how to handle event emitter classes?
export interface Spec extends TurboModule {
+getConstants: () => {|
// any constants it exports
|};
// add any exported methods
// Then add these 2 methods:
// Events
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
eric: I changed the comment for listeners to be generic
Also worth noting: there are some places in the code base that directly access constants in JS, they need to be changed to be grabbed from getConstants.
Example:
const DeviceInfo = require('./DeviceInfo');
dims = DeviceInfo.Dimensions;
becomes:
const DeviceInfo = require('./DeviceInfo');
dims = DeviceInfo.getConstants().Dimensions;
luckily, flow will complain about these if you do your typing right! So, just update the callsites.
Hey! I'd like to help with Linking
module.
Hi, I would like to help with DeviceInfo
@michalchudziak, @gedeagas both were done by &ericlewisβ PRs
PermissionAndroid/TimePickerAndroid are still open I think
I'll go with PermissionsAndroid
then
Sure, no problem! Is it possible to work on AccessibilityManager
& AccessibilityInfo
?
Edit: I'm working on it :)
I work on ToastAndroid
Taking Networking
Working on Timing
Working on WebSocketModule
~Taking ToastAndroid
~ someone already taken
Working on HeapCapture
Working on IntentAndroid
Working on ModalManager
@RSNara what about the case where the native method definition differs on iOS and Android?
This is what I did sofar for the close
method of WebSocketModule:
// Native close method definition on Android
declare function close(code: number, reason: string, socketID: number): void;
// Native close method definition on iOS
declare function close(socketID: number): void;
export interface Spec extends TurboModule {
+connect: (
url: string,
protocols: ?Array<string>,
options: ?{headers?: {origin?: string}},
socketID: number,
) => void;
+send: (message: string, socketID: number) => void;
+sendBinary: (base64String: string, socketID: number) => void;
+ping: (socketID: number) => void;
+close: typeof close;
// RCTEventEmitter
+addListener: (eventName: string) => void;
+removeListeners: (count: number) => void;
}
@jeanregisser I had similar issue with Networking module: https://github.com/facebook/react-native/pull/24892 and after consulting ideas with @rickhanlonii we decided to create two native modules for now: NativeNetworkingIOS
and NativeNetworkingAndroid
as even when using Platform.OS
check, Flow mixed 2 definitions resulting in errors of wrong signature being used.
Working on TimePickerAndroid
Working on TVNavigationEventEmitter
Working on SourceCode
~Working on NativeAnimatedModule
~ Already taken by @thymikee
Taking Taken by @thymikee .ExceptionsManager
Taking UIManager
Working on AlertManager
Taking ImageLoader
can I take DevSettings
?
I'll take PlatformConstants Platform.android.js
I will take JSCSamplingProfiler
Will take FileReaderModule
i'll take DevLoadingView
I will take JSDevSupport
Taking NativeAnimatedModule
Thank you all for doing the work! We're halfway there :)
Taking HeadlessJsTaskSupport
Will take I18nManagerModule
taking BlobModule
Working on DialogManagerAndroid
taking DatePickerAndroid
Taking DeviceEventManager
Taking ImageViewManager
taking AnimationDebugModule
@uqmessias:
Actually viewmanagers shouldnβt be typed, re: ImageViewManager
@fkgozali
I was almost finishing, but I'll stop it and keep the current code in a separated branch.
RedBox remaining.
@fkgozali can you check the ImageLoader
mapping ios and android files in the table: RCTImageEditingManager.m
and ImageLoaderModule.java
look like something wrong?
@yjose,
I was working on something related to this one
@uqmessias hmm ok
Actually` viewmanagers shouldnβt be typed, re: ImageViewManager
in this case, I think they mean ImageEditor
which have the correct ios and android files RCTImageEditingManager.m
and ImageEditingManager.java
?
cc @RSNara @fkgozali
@yjose i think you are correct
working on ImageEditor
We got 'em all i think
Taking Platform.ios.js
I would like to help with AppState
. Let's get started right away.
@mitulsavani AppState is already done and merged. Please have a look at "NativeModule List" section of the original post. Looks like the only one left is "TimePickerAndroid.ios.js" β feel free to grab it while it's free! (and please check https://github.com/facebook/react-native/pull/24897/files because it may overlap, so it may be better to cooperate with the author, @jeanregisser, on this one)
I would certainly like to claim that. Although i have no clue how and what needs to change/add, but I want to at least give a shot! @thymikee
@mitulsavani @thymikee indeed https://github.com/facebook/react-native/pull/24897 addresses TimePickerAndroid.ios.js
as well.
@thymikee anything left that I work on ?
Thanks all for the PRs, we're going through them one by one to ensure nothing else breaks in FB internal infra. Stay tuned!
It looks like ImageStore
got left out. For anyone interested, feel free to claim it.
I hereby claim for it.
DevSettings
was also left out, feel free to claim.
I will claim the βDevSettingβ
As of https://github.com/facebook/react-native/commit/18fededae085b53b01e54a7ed27e32c2318e7cae, all 42 PRs have now been merged! Thank you for all the contributions to this effort!
I'll close this issue for now.
In order to get a sense of how the codegen output looks like, https://github.com/facebook/react-native/commit/b1bf133d69cf06629fb1beeeaf23ccd418bec242 checked them in, based on all the Flow types you all have added. This is not used yet, and we're working on migrating each module to use it gradually.
Hi,
In the instructions, we can read:
Create a Spec file called NativeXYZ.js, where XYZ is the name of the NativeModule.
How strict is this rule?
There's no NativePlatformConstants.js
anywhere, rather there I see both NativePlatformConstantsIOS.js
and NativePlatformConstantsAndroid.js
. Is that going to through a wrench in the cogs?
https://github.com/react-native-community/discussions-and-proposals/issues/40#issuecomment-537056284
Most helpful comment
As of https://github.com/facebook/react-native/commit/18fededae085b53b01e54a7ed27e32c2318e7cae, all 42 PRs have now been merged! Thank you for all the contributions to this effort!
I'll close this issue for now.