Skip to content

Instantly share code, notes, and snippets.

@DomiR
Last active March 26, 2022 09:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DomiR/830f68b03ae7bf90165a2573e7957d64 to your computer and use it in GitHub Desktop.
Save DomiR/830f68b03ae7bf90165a2573e7957d64 to your computer and use it in GitHub Desktop.
Extend objective c react-native module

facebook/react-native#1908 (comment) @alinz, @brentvatne & @jaygarcia

So lets say you want to add eval method to WebView component. What you have to do is: use category feature in objective-c.

// RCTWebView+WebViewExBridge.h

#import "RCTWebView.h"

@interface RCTWebView (WebViewExBridge)
- (void)eval:(NSString *) value;
@end
RCTWebView+WebViewExBridge.m

#import "RCTWebView+WebViewExBridge.h"

@implementation RCTWebView (WebViewExBridge)

- (void)eval:(NSString *) value {
  //This is the only way to get access to private variables in RCTWebView
  UIWebView* _webView = [self valueForKey:@"_webView"];
  //////////////////////////////////////////////////////
  [_webView stringByEvaluatingJavaScriptFromString:value];
  NSLog(@"Called Eval %@", value);
}

@end
RCTWebViewManager+WebViewExManager.h

#import "RCTWebViewManager.h"

@interface RCTWebViewManager (WebViewExManager)

@end
//RCTWebViewManager+WebViewExManager.m

#import "RCTWebViewManager+WebViewExManager.h"
#import "RCTBridge.h"
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
#import "RCTWebView.h"
#import "RCTWebView+WebViewExBridge.h"

@implementation RCTWebViewManager (WebViewExManager)

//NOTE
//DO not include RCT_EXPORT_MODULE() here because RCTWebViewManager already has it and
//we are using category feature in objective-c

RCT_EXPORT_METHOD(eval:(NSNumber *)reactTag
                 value:(NSString*)value)
{
  [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
    RCTWebView *view = viewRegistry[reactTag];
    if (![view isKindOfClass:[RCTWebView class]]) {
      RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
    }
    [view eval:value];
  }];
}

@end

And the last one is our WebViewBrdige component which wraps WebView as a higher order component(Thanks @brentvatne).

// WebViewBridge.js
'use strict';

var React = require('react-native');

var {
  WebView,
  Component,
  NativeModules: {
    WebViewManager
  }
} = React;

var WEB_VIEW_BRIDGE_REF = 'WEBVIEW_BRIDGE';

class WebViewBridge extends Component {
  constructor(props) {
    super(props);
  }

  //exposing objective-c eval as evalScript
  evalScript(value) {
    var ref = this.refs[WEB_VIEW_BRIDGE_REF];
    //we need to get the handler of current WebView in order for our
    WebViewManager.eval(ref.getWebWiewHandle(), value);
  }

  render() {
    return (
      <WebView
        ref={WEB_VIEW_BRIDGE_REF}
        {...this.props}/>
    );
  }
}

module.exports = WebViewBridge;

Now what we can do is using the WebViewBridge as follows:

'use strict';

var React = require('react-native');
var WebViewBridge = require('./WebViewBridge.js');

var Sample2 = React.createClass({
  componentDidMount: function () {
    this.refs.['myWebViewBridge'].evalScript('window.alert("Booya!")');
  },
  render: function() {
    var url = "http://google.com";
    return (
      <WebViewBridge ref="myWebViewBridge" url={url}/>
    );
  }
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment