Skip to content

Commit

Permalink
Implement Android's dispatchViewManagerCommand interface on iOS
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nicklockwood authored and facebook-github-bot-7 committed Jan 6, 2016
1 parent 28c0240 commit 17df595
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 10 deletions.
29 changes: 26 additions & 3 deletions Libraries/BatchedBridge/BatchedBridgedModules/NativeModules.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand 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];
Expand All @@ -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;
},
});
}
});

Expand Down
19 changes: 16 additions & 3 deletions Libraries/Components/WebView/WebView.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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
);
},

/**
Expand Down
8 changes: 8 additions & 0 deletions React/Base/RCTBatchedBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ - (void)loadSource:(RCTSourceLoadBlock)_onSourceLoad
return _moduleClassesByID;
}

/**
* Used by RCTUIManager
*/
- (RCTModuleData *)moduleDataForName:(NSString *)moduleName
{
return _moduleDataByName[moduleName];
}

- (id)moduleForName:(NSString *)moduleName
{
RCTModuleData *moduleData = _moduleDataByName[moduleName];
Expand Down
6 changes: 6 additions & 0 deletions React/Base/RCTBridge+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
4 changes: 0 additions & 4 deletions React/Base/RCTModuleMethod.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,4 @@
JSMethodName:(NSString *)JSMethodName
moduleClass:(Class)moduleClass NS_DESIGNATED_INITIALIZER;

- (void)invokeWithBridge:(RCTBridge *)bridge
module:(id)module
arguments:(NSArray *)arguments;

@end
17 changes: 17 additions & 0 deletions React/Modules/RCTUIManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -907,6 +910,20 @@ - (void)_manageChildren:(NSNumber *)containerReactTag
}];
}

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) {
Expand Down

0 comments on commit 17df595

Please sign in to comment.