-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React Native “Plugins” for Native Libraries #125
Comments
I haven't fully digested this, but I have a couple of questions first:
I think it's pretty important that old libraries continue to work and new library releases can use the "plugin" system and still be usable on older versions of React Native. Ideally, this wouldn't be an issue but with some users having a hard time upgrading it's worth making sure we don't cause too much additional pain, if at all possible. |
The plugin annotation can be considered an "addon" to the actual module implementation. As hinted above in the FAQ section:
This means, as long as the fallback mechanism is still alive in core, old modules will continue to work.
We're not changing any native code syntax for these modules any time soon, so as long as new modules keep using the recommended syntax, they will continue to work without plugin annotation. In fact, this is how we migrate internal modules to the TurboModule/plugin system, all old syntax in code is still preserved atm. Of course, it's up to module owners if they want to move on to the modern mechanism without using any of the old syntax like That said, at some point in the distant future, the old mechanism will be deprecated for better maintainability. This won't happen any time soon though, not 2019, maybe 2020. |
|
Invoking the declared plugin function loads the classes, but not necessarily instantiate the instances (that will depend on the callsites). |
Background
The current system to register and load React Native NativeModules is not consistent across different platforms today, and within each platform there are more than one ways to do so. This creates inconsistent runtime behaviors depending on which method is used, including the startup performance of the React Native infra (bridge). For instance, in iOS, the system relies on a lot of runtime class and protocol lookups, while in Android, one has to explicitly list out the modules they need.
In order to unify this mechanism we need to standardize the registration process so that:
The Problem with iOS NativeModules Registration TODAY
RCT_EXPORT_MODULE()
adds+load
method to the ObjC class, which means:NSBundle
's, and +load unfortunately force loads all theNSBundle
's, again bad for app startup-ObjC
flag"RCT"
and"RK"
prefix in the class names get stripped magicallyThe Problem with Android NativeModules Registration Today
ReactPackage
today:The “Plugin” Concept
The two main concepts we'd like to apply here are:
For the sake of the discussion, let's call this system the “Native Library Plugin”, or just “plugin”.
What is A Plugin?
A plugin provides:
NSBundle
in iOS)Plugin Schema
To define a plugin we need a simple schema containing:
Here's a simplified example definition using BUCK syntax for iOS NativeModule. Let's call it
RCT_NATIVE_MODULE_PROVIDER_SOCKET
.Note that the syntax will depend on the build system in use. With the schema defined, each library will need to define what “socket(s)” it provides.
NativeModules Example
To describe this more easily, let's use an ObjC NativeModule as an example:
RCTSampleModule
. Consider the followingBUCK
file forRCTSampleModule
:For the 3 things a plugin needs to provide:
RCT_EXPORT_MODULE()
in each class, we annotate the build config for that module:SampleModule
- this is the name used everywhere, including JS.RCTSampleModule
Class RCTSampleModuleCls()
Then with all the codegen output, we can write this simple C lookup function for the entire NativeModules in the app:
The hosting app can simply call
RCTNativeModulePluginClassProvider()
and not worry about how the modules are registered. Then theRCTSampleModule.mm
will implement the C function:In this example, once the system finds the class
RCTSampleModule
by invokingRCTSampleModuleCls()
via the plugin system, it knows how to initialize the NativeModule properly.Further, for some use cases, one can get a list of registered plugins as follow:
React Native Core Specific Plugins
There are a few core React Native functionalities that will benefit from plugins. At least each of the following shall define its own plugin schema:
FAQs
Q: What do you mean by having build system specific syntax for the socket definition?
There are many ways to build an app, e.g. using Buck, CocoaPods, gradle, Xcode, etc. Each of the integrations will need its own way to define the plugin sockets. As long as the codegen output is consistent across this system, the syntax for each system can be flexible.
Q: Can a library define multiple sockets? Or is it limited to one?
Each library can define 0 or more sockets of the same type, as long as the identifiers do not collide. For example, there shall be only one definition of “SampleModule” throughout the entire app. In case there are different implementation of such modules for different apps, the build dependency graph needs to make sure only exactly 1 of “SampleModule” socket is provided for each app.
Q: Is this a hard blocker for Fabric and TurboModule projects?
No, both projects have their own fallback mechanism to load the modules/components. For the initial rollout, those fallbacks will still be used. But long term, we'd like to unify the registration with this one plugin system, deprecating these fallbacks over time. For example, the legacy way of getting a module class from its name shall be deprecated eventually.
Q: It seems like only big apps may get into the issues listed above, why have this by default then?
It is more for unifying the code paths regardless of the apps you're building. Historically we had many ways to register things, as described above, and it's not always easy to keep all mechanism up-to-date and consistent. Also, with this system, we hope that by default the mechanism is as performant as it can be.
Q: How is this used at Facebook?
Facebook has its own internal implementation of the plugin system, using BUCK, and is very FB-specific. In fact, TurboModules and Fabric components are using these system internally. This is why we need help building the same system in OSS environment so that there's only one system being used everywhere.
Q: There's already autolinking effort, will this be compatible?
Yes, in fact, this can be additional features on top of autolinking, not a complete replacement.
The text was updated successfully, but these errors were encountered: