react-native/React/Base/RCTBridge.h
Ramanpreet Nara 0ceefb40d5 Enable module lookup in TurboModules
Summary:
NativeModules are instantiated by the bridge. If they choose, they can capture the bridge instance that instantiated them. From within the NativeModule, the bridge can then be used to lookup other NativeModules. TurboModules have no way to do such a lookup.

Both NativeModules and TurboModules need to be able to query for one another. Therefore, we have four cases:
1. NativeModule accesses NativeModule.
2. NativeModule accesses TurboModule.
3. TurboModule accesses NativeModule.
4. TurboModule accesses TurboModule.

In summary, this solution extends the bridge to support querying TurboModules. It also introduces a `RCTTurboModuleLookupDelegate` protocol, which, implemented by `RCTTurboModuleManager`, supports querying TurboModules:
```
protocol RCTTurboModuleLookupDelegate <NSObject>
- (id)moduleForName:(NSString *)moduleName;
- (id)moduleForName:(NSString *)moduleName warnOnLookupFailure:(BOOL)warnOnLookupFailure;
- (BOOL)moduleIsInitialized:(NSString *)moduleName
end
```

If TurboModules want to query other TurboModules, then they need to implement this protocol and synthesize `turboModuleLookupDelegate`:

```
protocol RCTTurboModuleWithLookupCapabilities
property (nonatomic, weak) id<RCTTurboModuleLookupDelegate> turboModuleLookupDelegate;
end
```

NativeModules will continue to use `RCTBridge` to access other NativeModules. Nothing needs to change.

When we attach the bridge to `RCTTurboModuleManager`, we also attach `RCTTurboModuleManager` to the bridge as a `RCTTurboModuleLookupDelegate`. This allows the bridge to query TurboModules, which enables our NativeModules to transparently (i.e: without any NativeModule code modification) query TurboModules.

In an ideal world, all modules would be TurboModules. Until then, we're going to require that TurboModules use the bridge to query for NativeModules or TurboModules.

`RCTTurboModuleManager` keeps a map of all TurboModules that we instantiated. We simply search in this map and return the TurboModule.

This setup allows us to switch NativeModules to TurboModules without compromising their ability to use the bridge to search for other NativeModules (and TurboModules). When we write new TurboModules, we can have them use `RCTTurboModuleLookupDelegate` to do access other TurboModules. Eventually, after we migrate all NativeModules to TurboModules, we can migrate all old callsites to use `RCTTurboModuleLookupDelegate`.

Reviewed By: fkgozali

Differential Revision: D13553186

fbshipit-source-id: 4d0488eef081332c8b70782e1337eccf10717dae
2019-01-31 11:35:05 -08:00

239 lines
7.9 KiB
Objective-C

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTBridgeDelegate.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTDefines.h>
#import <React/RCTFrameUpdate.h>
#import <React/RCTInvalidating.h>
@class JSValue;
@class RCTBridge;
@class RCTEventDispatcher;
@class RCTPerformanceLogger;
/**
* This notification fires when the bridge initializes.
*/
RCT_EXTERN NSString *const RCTJavaScriptWillStartLoadingNotification;
/**
* This notification fires when the bridge starts executing the JS bundle.
*/
RCT_EXTERN NSString *const RCTJavaScriptWillStartExecutingNotification;
/**
* This notification fires when the bridge has finished loading the JS bundle.
*/
RCT_EXTERN NSString *const RCTJavaScriptDidLoadNotification;
/**
* This notification fires when the bridge failed to load the JS bundle. The
* `error` key can be used to determine the error that occurred.
*/
RCT_EXTERN NSString *const RCTJavaScriptDidFailToLoadNotification;
/**
* This notification fires each time a native module is instantiated. The
* `module` key will contain a reference to the newly-created module instance.
* Note that this notification may be fired before the module is available via
* the `[bridge moduleForClass:]` method.
*/
RCT_EXTERN NSString *const RCTDidInitializeModuleNotification;
/**
* This notification fires just before the bridge starts processing a request to
* reload.
*/
RCT_EXTERN NSString *const RCTBridgeWillReloadNotification;
/**
* This notification fires just before the bridge begins downloading a script
* from the packager.
*/
RCT_EXTERN NSString *const RCTBridgeWillDownloadScriptNotification;
/**
* This notification fires just after the bridge finishes downloading a script
* from the packager.
*/
RCT_EXTERN NSString *const RCTBridgeDidDownloadScriptNotification;
/**
* Key for the RCTSource object in the RCTBridgeDidDownloadScriptNotification
* userInfo dictionary.
*/
RCT_EXTERN NSString *const RCTBridgeDidDownloadScriptNotificationSourceKey;
/**
* Key for the bridge description (NSString_ in the
* RCTBridgeDidDownloadScriptNotification userInfo dictionary.
*/
RCT_EXTERN NSString *const RCTBridgeDidDownloadScriptNotificationBridgeDescriptionKey;
/**
* This block can be used to instantiate modules that require additional
* init parameters, or additional configuration prior to being used.
* The bridge will call this block to instatiate the modules, and will
* be responsible for invalidating/releasing them when the bridge is destroyed.
* For this reason, the block should always return new module instances, and
* module instances should not be shared between bridges.
*/
typedef NSArray<id<RCTBridgeModule>> *(^RCTBridgeModuleListProvider)(void);
/**
* This function returns the module name for a given class.
*/
RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
/**
* Experimental.
* Check/set if JSI-bound NativeModule is enabled. By default it's off.
*/
RCT_EXTERN BOOL RCTTurboModuleEnabled(void);
RCT_EXTERN void RCTEnableTurboModule(BOOL enabled);
/**
* Async batched bridge used to communicate with the JavaScript application.
*/
@interface RCTBridge : NSObject <RCTInvalidating>
/**
* Creates a new bridge with a custom RCTBridgeDelegate.
*
* All the interaction with the JavaScript context should be done using the bridge
* instance of the RCTBridgeModules. Modules will be automatically instantiated
* using the default contructor, but you can optionally pass in an array of
* pre-initialized module instances if they require additional init parameters
* or configuration.
*/
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
launchOptions:(NSDictionary *)launchOptions;
/**
* DEPRECATED: Use initWithDelegate:launchOptions: instead
*
* The designated initializer. This creates a new bridge on top of the specified
* executor. The bridge should then be used for all subsequent communication
* with the JavaScript code running in the executor. Modules will be automatically
* instantiated using the default contructor, but you can optionally pass in an
* array of pre-initialized module instances if they require additional init
* parameters or configuration.
*/
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
moduleProvider:(RCTBridgeModuleListProvider)block
launchOptions:(NSDictionary *)launchOptions;
/**
* This method is used to call functions in the JavaScript application context.
* It is primarily intended for use by modules that require two-way communication
* with the JavaScript code. Safe to call from any thread.
*/
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
- (void)enqueueJSCall:(NSString *)module method:(NSString *)method args:(NSArray *)args completion:(dispatch_block_t)completion;
/**
* This method registers the file path of an additional JS segment by its ID.
*
* @experimental
*/
- (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path;
/**
* Retrieve a bridge module instance by name or class. Note that modules are
* lazily instantiated, so calling these methods for the first time with a given
* module name/class may cause the class to be sychronously instantiated,
* potentially blocking both the calling thread and main thread for a short time.
*
* Note: This method does NOT lazily load the particular module if it's not yet loaded.
*/
- (id)moduleForName:(NSString *)moduleName;
- (id)moduleForName:(NSString *)moduleName lazilyLoadIfNecessary:(BOOL)lazilyLoad;
// Note: This method lazily load the module as necessary.
- (id)moduleForClass:(Class)moduleClass;
/**
* When a NativeModule performs a lookup for a TurboModule, we need to query
* the lookupDelegate.
*/
- (void)setRCTTurboModuleLookupDelegate:(id<RCTTurboModuleLookupDelegate>)turboModuleLookupDelegate;
/**
* Convenience method for retrieving all modules conforming to a given protocol.
* Modules will be sychronously instantiated if they haven't already been,
* potentially blocking both the calling thread and main thread for a short time.
*/
- (NSArray *)modulesConformingToProtocol:(Protocol *)protocol;
/**
* Test if a module has been initialized. Use this prior to calling
* `moduleForClass:` or `moduleForName:` if you do not want to cause the module
* to be instantiated if it hasn't been already.
*/
- (BOOL)moduleIsInitialized:(Class)moduleClass;
/**
* All registered bridge module classes.
*/
@property (nonatomic, copy, readonly) NSArray<Class> *moduleClasses;
/**
* URL of the script that was loaded into the bridge.
*/
@property (nonatomic, strong, readonly) NSURL *bundleURL;
/**
* The class of the executor currently being used. Changes to this value will
* take effect after the bridge is reloaded.
*/
@property (nonatomic, strong) Class executorClass;
/**
* The delegate provided during the bridge initialization
*/
@property (nonatomic, weak, readonly) id<RCTBridgeDelegate> delegate;
/**
* The launch options that were used to initialize the bridge.
*/
@property (nonatomic, copy, readonly) NSDictionary *launchOptions;
/**
* Use this to check if the bridge is currently loading.
*/
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
/**
* Use this to check if the bridge has been invalidated.
*/
@property (nonatomic, readonly, getter=isValid) BOOL valid;
/**
* Link to the Performance Logger that logs React Native perf events.
*/
@property (nonatomic, readonly, strong) RCTPerformanceLogger *performanceLogger;
/**
* Reload the bundle and reset executor & modules. Safe to call from any thread.
*/
- (void)reload;
/**
* Inform the bridge, and anything subscribing to it, that it should reload.
*/
- (void)requestReload __deprecated_msg("Call reload instead");
/**
* Says whether bridge has started receiving calls from javascript.
*/
- (BOOL)isBatchActive;
@end