-
Notifications
You must be signed in to change notification settings - Fork 24.3k
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
[NativeModules] Support for loading React and/or native modules as a dynamic framework #1228
Conversation
This adds support for dynamically loading React as a framework. It introduces several changes to RCTBridge.m - extraction of strings from a mach_header section has been encapsulated in a helper class `RCTHeaderStringExtractor`. - instances of this class are stored in an `NSSet` and uniqued by the address of their associated mach_header. - a registration function `RCTRegisterModuleProviderContainingAddress(const void *address)` registers the binary containing a given address as a "module provider" and creates a `RCTHeaderStringExtractor` for the binary's mach_header. A convienience helper function `RCTRegisterModuleProvider(void)` does the same, but registers the caller instead of requiring an explicit address. - the RCT_EXPORT_MODULE macro now includes a definition for `+ (void) load` that calls `RCTRegisterModuleProvider` - this allows registration of all module providers without changes to the existing api, but does potentially break native modules that already provide an implementation of `load` - during the static `RCTBridge` initialization, all registered module providers are scanned, rather than just the binary in which `RCTBridge` is contained.
This is essentially the same as the solution I was planning, but thanks for doing the leg work! The +load limitation is annoying, although in practice it can be worked around fairly easily (e.g. by adding custom load method inside a category). |
Awesome, glad to help 😄 |
This adds another registration function `RCTRegisterMainAppModuleProvider(void)` that attempts to register the binary containing the `main()` function as a React Native module provider. This allows a project to link against a static library that contains a React Native module, without itself providing any modules, or having to explicitly register as a module provider. Without this, the static library containing the module is registered, but the `RCTExport`, etc. strings are actually linked into the app binary and aren't found.
The last commit adds automatic registration of the binary containing the |
👍 super excited for this |
@nicklockwood - any progress here? this looks like a nice feature 😄 |
following this, will this mean I could create a Framework in swift and include in my project? At the moment I'm trying to do that and getting |
@mebinum yes, this supports swift frameworks. My main motivation for this was to explore integrating react native into an existing swift app, where most of the code is contained in a framework that's shared between app and extension targets. Using this branch I was able to define React native modules in the framework and use them from an |
Awesome @yusefnapora look forward to this getting merged. Anyone know what the ETA on that is? |
@nicklockwood I've been busy at work and haven't kept up with this, but it looks like the new module loading system supports dynamic linkage without the crazy hacks in this PR. 😸 Not sure exactly when the change was made, but I did a quick test on 0.7.0-rc.2. Modules defined in a shared framework load without a hitch, and React can be included as a pod using the I'm going to close this and celebrate by poking at all the new features that have cropped up while I've been chained to Xcode. Excellent work, react team; the new module loading system is very nice and clean! |
I still ran into the same issues as @mebinum. @yusefnapora Is there an example that i can follow to see how to get it work? Thanks! |
…rsion Ensure we use 0.68 for testing new apps, not 0.69 or later
This adds support for dynamically loading React as a framework. If accepted, closes #579. IntegrationTests all run successfully.
This introduces several changes to RCTBridge.m
RCTHeaderStringExtractor
.NSSet
and uniqued by the address of their associated mach_header.RCTRegisterModuleProviderContainingAddress(const void *address)
registers the binary containing a given address as a "module provider" and creates aRCTHeaderStringExtractor
for the binary's mach_header. A convienience helper functionRCTRegisterModuleProvider(void)
does the same, but registers the caller instead of requiring an explicit address.RCTBridge
initialization, all registered module providers are scanned, rather than just the binary in whichRCTBridge
is contained.In RCTBridgeModule.h:
+ (void) load
that callsRCTRegisterModuleProvider
- this allows registration of all module providers without changes to the existing api, but does potentially break native modules that already provide an implementation ofload
A simple example project is a available at https://github.com/yusefnapora/react-dynamic-linking-example - it uses Cocoapods to install this branch with the
use_frameworks!
directive, which forces React to be compiled as a dynamic framework instead of a static library. React components are loaded from both the framework and the app binary.Concerns:
My biggest problem with this approach is the
load
hook for native modules; any existing native modules that currently provide an implementation of+ (void)load
will refuse to compile. I chose this approach because it doesn't require any explicit registration of the binary containing the modules; when the class loader invokesload
, that will happen automatically for all "module providers". As a result, this approach doesn't require "module providers" to know or care about whether they'll be linked dynamically or statically.I also haven't measured the performance impact of the additional calls to
load
andRCTRegisterModuleProvider
for each native module. I suspect it's minimal, as the only work involved is callingdladdr
to obtain the mach header, and creation of aRCTHeaderStringExtractor
, which is fairly lightweight. While aRCTHeaderStringExtractor
is created for each invocation ofRCTRegisterModuleProvider
, only one is stored per mach header, as they are stored in anNSSet
andhash
ed by the mach header address.Perhaps a compromise to allow native modules to define a
load
hook would be to check whether the class responds to a (hypothetical)rct_load
class method and, if so, call it from the macro-providedload
method.Please let me know if anything is unclear or I can do anything to help make this work.