Implement Android's dispatchViewManagerCommand interface on iOS

Summary:
public
Android implement ViewManager methods via a dispatch method on UIManager, whereas iOS implements them by exposing the methods on the view manager modules directly.

This diff polyfills Android's implementation on top of the iOS implementation, allowing the same JS API to be used for both.

Reviewed By: javache

Differential Revision: D2803020

fb-gh-sync-id: 0da0544e593dc936467d16ce957a77f7ca41355b
This commit is contained in:
Nick Lockwood 2016-01-06 05:57:25 -08:00 committed by facebook-github-bot-7
parent 28c0240361
commit 17df595e32
6 changed files with 73 additions and 10 deletions

View File

@ -56,8 +56,8 @@ Object.keys(RemoteModules).forEach((moduleName) => {
});
/**
* Copies the ViewManager constants into UIManager. This is only
* needed for iOS, which puts the constants in the ViewManager
* Copies the ViewManager constants and commands into UIManager. This is
* only needed for iOS, which puts the constants in the ViewManager
* namespace instead of UIManager, unlike Android.
*
* We'll eventually move this logic to UIManager.js, once all
@ -67,12 +67,16 @@ Object.keys(RemoteModules).forEach((moduleName) => {
const UIManager = NativeModules.UIManager;
UIManager && Object.keys(UIManager).forEach(viewName => {
const viewConfig = UIManager[viewName];
const constants = {};
if (viewConfig.Manager) {
let constants;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Constants', {
enumerable: true,
get: () => {
if (constants) {
return constants;
}
constants = {};
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
viewManager && Object.keys(viewManager).forEach(key => {
const value = viewManager[key];
@ -83,6 +87,25 @@ UIManager && Object.keys(UIManager).forEach(viewName => {
return constants;
},
});
let commands;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Commands', {
enumerable: true,
get: () => {
if (commands) {
return commands;
}
commands = {};
const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
viewManager && Object.keys(viewManager).forEach((key, index) => {
const value = viewManager[key];
if (typeof value === 'function') {
commands[key] = index;
}
});
return commands;
},
});
}
});

View File

@ -16,6 +16,7 @@ var EdgeInsetsPropType = require('EdgeInsetsPropType');
var React = require('React');
var StyleSheet = require('StyleSheet');
var Text = require('Text');
var UIManager = require('UIManager');
var View = require('View');
var invariant = require('invariant');
@ -240,15 +241,27 @@ var WebView = React.createClass({
},
goForward: function() {
RCTWebViewManager.goForward(this.getWebViewHandle());
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.goForward,
null
);
},
goBack: function() {
RCTWebViewManager.goBack(this.getWebViewHandle());
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.goBack,
null
);
},
reload: function() {
RCTWebViewManager.reload(this.getWebViewHandle());
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.reload,
null
);
},
/**

View File

@ -222,6 +222,14 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
return _moduleClassesByID;
}
/**
* Used by RCTUIManager
*/
- (RCTModuleData *)moduleDataForName:(NSString *)moduleName
{
return _moduleDataByName[moduleName];
}
- (id)moduleForName:(NSString *)moduleName
{
RCTModuleData *moduleData = _moduleDataByName[moduleName];

View File

@ -63,6 +63,12 @@
*/
- (void)dispatchBlock:(dispatch_block_t)block queue:(dispatch_queue_t)queue;
/**
* Get the module data for a given module name. Used by UIManager to implement
* the `dispatchViewManagerCommand` method.
*/
- (RCTModuleData *)moduleDataForName:(NSString *)moduleName;
/**
* Systrace profiler toggling methods exposed for the RCTDevMenu
*/

View File

@ -31,8 +31,4 @@
JSMethodName:(NSString *)JSMethodName
moduleClass:(Class)moduleClass NS_DESIGNATED_INITIALIZER;
- (void)invokeWithBridge:(RCTBridge *)bridge
module:(id)module
arguments:(NSArray *)arguments;
@end

View File

@ -16,12 +16,15 @@
#import "RCTAnimationType.h"
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTBridge+Private.h"
#import "RCTComponent.h"
#import "RCTComponentData.h"
#import "RCTConvert.h"
#import "RCTDefines.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTModuleData.h"
#import "RCTModuleMethod.h"
#import "RCTProfile.h"
#import "RCTRootView.h"
#import "RCTRootViewInternal.h"
@ -907,6 +910,20 @@ RCT_EXPORT_METHOD(findSubviewIn:(nonnull NSNumber *)reactTag atPoint:(CGPoint)po
}];
}
RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag
commandID:(NSInteger)commandID
commandArgs:(NSArray<id> *)commandArgs)
{
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
RCTComponentData *componentData = _componentDataByName[shadowView.viewName];
Class managerClass = componentData.managerClass;
RCTModuleData *moduleData = [_bridge moduleDataForName:RCTBridgeModuleNameForClass(managerClass)];
id<RCTBridgeMethod> method = moduleData.methods[commandID];
NSArray *args = [@[reactTag] arrayByAddingObjectsFromArray:commandArgs];
[method invokeWithBridge:_bridge module:componentData.manager arguments:args];
}
- (void)partialBatchDidFlush
{
if (self.unsafeFlushUIChangesBeforeBatchEnds) {