├── .gitignore ├── BridgeableWebView.ios.js ├── LICENSE ├── README.md ├── RNBridgeableWebView.h ├── RNBridgeableWebView.m ├── RNBridgeableWebViewManager.h ├── RNBridgeableWebViewManager.m ├── RNBridgeableWebview.xcodeproj └── project.pbxproj └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/**/* 2 | RNBridgeableWebview.xcodeproj/xcuserdata/**/* 3 | RNBridgeableWebview.xcodeproj/project.xcworkspace/**/* 4 | npm-debug.log -------------------------------------------------------------------------------- /BridgeableWebView.ios.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * @flow 9 | */ 10 | 'use strict'; 11 | 12 | var ActivityIndicatorIOS = require('ActivityIndicatorIOS'); 13 | var EdgeInsetsPropType = require('EdgeInsetsPropType'); 14 | var React = require('React'); 15 | var StyleSheet = require('StyleSheet'); 16 | var Text = require('Text'); 17 | var View = require('View'); 18 | 19 | var invariant = require('invariant'); 20 | var keyMirror = require('keyMirror'); 21 | var requireNativeComponent = require('requireNativeComponent'); 22 | 23 | var PropTypes = React.PropTypes; 24 | var RCTWebViewManager = require('NativeModules').RNBridgeableWebViewManager; 25 | var BGWASH = 'rgba(255,255,255,0.8)'; 26 | var RCT_WEBVIEW_REF = 'webview'; 27 | 28 | var WebViewState = keyMirror({ 29 | IDLE: null, 30 | LOADING: null, 31 | ERROR: null, 32 | }); 33 | 34 | var NavigationType = { 35 | click: RCTWebViewManager.NavigationType.LinkClicked, 36 | formsubmit: RCTWebViewManager.NavigationType.FormSubmitted, 37 | backforward: RCTWebViewManager.NavigationType.BackForward, 38 | reload: RCTWebViewManager.NavigationType.Reload, 39 | formresubmit: RCTWebViewManager.NavigationType.FormResubmitted, 40 | other: RCTWebViewManager.NavigationType.Other, 41 | }; 42 | 43 | type ErrorEvent = { 44 | domain: any; 45 | code: any; 46 | description: any; 47 | } 48 | 49 | type Event = Object; 50 | 51 | var defaultRenderLoading = () => ( 52 | 53 | 54 | 55 | ); 56 | var defaultRenderError = (errorDomain, errorCode, errorDesc) => ( 57 | 58 | 59 | Error loading page 60 | 61 | 62 | {'Domain: ' + errorDomain} 63 | 64 | 65 | {'Error Code: ' + errorCode} 66 | 67 | 68 | {'Description: ' + errorDesc} 69 | 70 | 71 | ); 72 | 73 | var WebView = React.createClass({ 74 | statics: { 75 | NavigationType: NavigationType, 76 | }, 77 | 78 | propTypes: { 79 | url: PropTypes.string, 80 | html: PropTypes.string, 81 | renderError: PropTypes.func, // view to show if there's an error 82 | renderLoading: PropTypes.func, // loading indicator to show 83 | bounces: PropTypes.bool, 84 | scrollEnabled: PropTypes.bool, 85 | automaticallyAdjustContentInsets: PropTypes.bool, 86 | shouldInjectAJAXHandler: PropTypes.bool, 87 | contentInset: EdgeInsetsPropType, 88 | onNavigationStateChange: PropTypes.func, 89 | onWebviewMessageSent: PropTypes.func, 90 | startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load 91 | style: View.propTypes.style, 92 | /** 93 | * Used for android only, JS is enabled by default for WebView on iOS 94 | */ 95 | javaScriptEnabledAndroid: PropTypes.bool, 96 | }, 97 | 98 | getInitialState: function() { 99 | return { 100 | viewState: WebViewState.IDLE, 101 | lastErrorEvent: (null: ?ErrorEvent), 102 | startInLoadingState: true, 103 | }; 104 | }, 105 | 106 | componentWillMount: function() { 107 | if (this.props.startInLoadingState) { 108 | this.setState({viewState: WebViewState.LOADING}); 109 | } 110 | }, 111 | 112 | render: function() { 113 | var otherView = null; 114 | 115 | if (this.state.viewState === WebViewState.LOADING) { 116 | otherView = (this.props.renderLoading || defaultRenderLoading)(); 117 | } else if (this.state.viewState === WebViewState.ERROR) { 118 | var errorEvent = this.state.lastErrorEvent; 119 | invariant( 120 | errorEvent != null, 121 | 'lastErrorEvent expected to be non-null' 122 | ); 123 | otherView = (this.props.renderError || defaultRenderError)( 124 | errorEvent.domain, 125 | errorEvent.code, 126 | errorEvent.description 127 | ); 128 | } else if (this.state.viewState !== WebViewState.IDLE) { 129 | console.error( 130 | 'RNBridgeableWebView invalid state encountered: ' + this.state.loading 131 | ); 132 | } 133 | 134 | var webViewStyles = [styles.container, styles.webView, this.props.style]; 135 | if (this.state.viewState === WebViewState.LOADING || 136 | this.state.viewState === WebViewState.ERROR) { 137 | // if we're in either LOADING or ERROR states, don't show the webView 138 | webViewStyles.push(styles.hidden); 139 | } 140 | 141 | var webView = 142 | ; 158 | 159 | return ( 160 | 161 | {webView} 162 | {otherView} 163 | 164 | ); 165 | }, 166 | 167 | goForward: function() { 168 | RCTWebViewManager.goForward(this.getWebWiewHandle()); 169 | }, 170 | 171 | goBack: function() { 172 | RCTWebViewManager.goBack(this.getWebWiewHandle()); 173 | }, 174 | 175 | reload: function() { 176 | RCTWebViewManager.reload(this.getWebWiewHandle()); 177 | }, 178 | 179 | /** 180 | * We return an event with a bunch of fields including: 181 | * url, title, loading, canGoBack, canGoForward 182 | */ 183 | updateNavigationState: function(event: Event) { 184 | if (this.props.onNavigationStateChange) { 185 | this.props.onNavigationStateChange(event.nativeEvent); 186 | } 187 | }, 188 | 189 | getWebWiewHandle: function(): any { 190 | return React.findNodeHandle(this.refs[RCT_WEBVIEW_REF]); 191 | }, 192 | 193 | onLoadingStart: function(event: Event) { 194 | this.updateNavigationState(event); 195 | }, 196 | 197 | onLoadingError: function(event: Event) { 198 | event.persist(); // persist this event because we need to store it 199 | console.error('Encountered an error loading page', event.nativeEvent); 200 | 201 | this.setState({ 202 | lastErrorEvent: event.nativeEvent, 203 | viewState: WebViewState.ERROR 204 | }); 205 | }, 206 | 207 | onLoadingFinish: function(event: Event) { 208 | this.setState({ 209 | viewState: WebViewState.IDLE, 210 | }); 211 | this.updateNavigationState(event); 212 | }, 213 | 214 | onWebViewMessageSent: function(event: Event) { 215 | if (this.props.onWebViewMessageSent) { 216 | this.props.onWebViewMessageSent(event.nativeEvent); 217 | } 218 | } 219 | }); 220 | 221 | var RNBridgeableWebView = requireNativeComponent('RNBridgeableWebView', WebView); 222 | 223 | var styles = StyleSheet.create({ 224 | container: { 225 | flex: 1, 226 | }, 227 | errorContainer: { 228 | flex: 1, 229 | justifyContent: 'center', 230 | alignItems: 'center', 231 | backgroundColor: BGWASH, 232 | }, 233 | errorText: { 234 | fontSize: 14, 235 | textAlign: 'center', 236 | marginBottom: 2, 237 | }, 238 | errorTextTitle: { 239 | fontSize: 15, 240 | fontWeight: '500', 241 | marginBottom: 10, 242 | }, 243 | hidden: { 244 | height: 0, 245 | flex: 0, // disable 'flex:1' when hiding a View 246 | }, 247 | loadingView: { 248 | backgroundColor: BGWASH, 249 | flex: 1, 250 | justifyContent: 'center', 251 | alignItems: 'center', 252 | }, 253 | webView: { 254 | backgroundColor: '#ffffff', 255 | } 256 | }); 257 | 258 | module.exports = WebView; 259 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Tom Hastjarjanto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## react-native-bridgeable-webview 2 | 3 | A `` component replacement for react-native 4 | 5 | Requires react-native == 0.6 6 | 7 | ### Add it to your project 8 | 9 | 1. Run `npm install react-native-bridgeable-webview --save` 10 | 2. Open your project in XCode, right click on `Libraries` and click `Add Files to "Your Project Name"` 11 | * ![Screenshot](http://url.brentvatne.ca/jQp8.png) ![Screenshot](http://url.brentvatne.ca/1gqUD.png) (use the RNBridgeableWebview project rather than the one pictured in screenshot). 12 | 3. Add `libRNBridgeableWebview.a` to `Build Phases -> Link Binary With Libraries` 13 | ![(Screenshot)](http://url.brentvatne.ca/g9Wp.png). 14 | 5. Whenever you want to use it within React code now you can: `var WebView = require('react-native-bridgeable-webview');` 15 | 16 | 17 | ## Usage 18 | ``` 19 | var WebView = require('react-native-bridgeable-webview'); 20 | ``` 21 | 22 | It is the exact same component as `WebView` except it offers a custom url scheme that acts as message bridge and a method `onWebViewMessageSent` to respond to calls made from the webview. 23 | 24 | In your react-native code you can include the following snippet as a `WebView` replacement: 25 | 26 | ```jsx 27 | 37 | ``` 38 | -------------------------------------------------------------------------------- /RNBridgeableWebView.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "RCTView.h" 11 | 12 | extern NSString *const RNBridgeableWebViewMessageSent; 13 | 14 | @class RCTEventDispatcher; 15 | 16 | @interface RNBridgeableWebView : RCTView 17 | 18 | @property (nonatomic, strong) NSURL *URL; 19 | @property (nonatomic, assign) UIEdgeInsets contentInset; 20 | @property (nonatomic, assign) BOOL shouldInjectAJAXHandler; 21 | @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; 22 | 23 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; 24 | 25 | - (void)goForward; 26 | - (void)goBack; 27 | - (void)reload; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /RNBridgeableWebView.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "RNBridgeableWebView.h" 11 | 12 | #import 13 | 14 | #import "RCTAutoInsetsProtocol.h" 15 | #import "RCTEventDispatcher.h" 16 | #import "RCTLog.h" 17 | #import "RCTUtils.h" 18 | #import "RCTView.h" 19 | #import "UIView+React.h" 20 | 21 | @interface RNBridgeableWebView () 22 | 23 | @end 24 | 25 | NSString *const RNBridgeableWebViewMessageSent = @"messageSent"; 26 | 27 | @implementation RNBridgeableWebView 28 | { 29 | RCTEventDispatcher *_eventDispatcher; 30 | UIWebView *_webView; 31 | } 32 | 33 | - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher 34 | { 35 | if ((self = [super initWithFrame:CGRectZero])) { 36 | super.backgroundColor = [UIColor clearColor]; 37 | _automaticallyAdjustContentInsets = YES; 38 | _contentInset = UIEdgeInsetsZero; 39 | _eventDispatcher = eventDispatcher; 40 | _webView = [[UIWebView alloc] initWithFrame:self.bounds]; 41 | _webView.delegate = self; 42 | [self addSubview:_webView]; 43 | } 44 | return self; 45 | } 46 | 47 | - (void)goForward 48 | { 49 | [_webView goForward]; 50 | } 51 | 52 | - (void)goBack 53 | { 54 | [_webView goBack]; 55 | } 56 | 57 | - (void)reload 58 | { 59 | [_webView reload]; 60 | } 61 | 62 | - (void)setURL:(NSURL *)URL 63 | { 64 | // Because of the way React works, as pages redirect, we actually end up 65 | // passing the redirect urls back here, so we ignore them if trying to load 66 | // the same url. We'll expose a call to 'reload' to allow a user to load 67 | // the existing page. 68 | if ([URL isEqual:_webView.request.URL]) { 69 | return; 70 | } 71 | if (!URL) { 72 | // Clear the webview 73 | [_webView loadHTMLString:nil baseURL:nil]; 74 | return; 75 | } 76 | [_webView loadRequest:[NSURLRequest requestWithURL:URL]]; 77 | } 78 | 79 | - (void)setHTML:(NSString *)HTML 80 | { 81 | [_webView loadHTMLString:HTML baseURL:nil]; 82 | } 83 | 84 | - (void)layoutSubviews 85 | { 86 | [super layoutSubviews]; 87 | _webView.frame = self.bounds; 88 | [RCTView autoAdjustInsetsForView:self 89 | withScrollView:_webView.scrollView 90 | updateOffset:YES]; 91 | } 92 | 93 | - (void)setContentInset:(UIEdgeInsets)contentInset 94 | { 95 | _contentInset = contentInset; 96 | [RCTView autoAdjustInsetsForView:self 97 | withScrollView:_webView.scrollView 98 | updateOffset:NO]; 99 | } 100 | 101 | - (void)setBackgroundColor:(UIColor *)backgroundColor 102 | { 103 | CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor); 104 | self.opaque = _webView.opaque = (alpha == 1.0); 105 | _webView.backgroundColor = backgroundColor; 106 | } 107 | 108 | - (UIColor *)backgroundColor 109 | { 110 | return _webView.backgroundColor; 111 | } 112 | 113 | - (NSMutableDictionary *)baseEvent 114 | { 115 | NSURL *url = _webView.request.URL; 116 | NSString *title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 117 | NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary: @{ 118 | @"target": self.reactTag, 119 | @"url": url ? [url absoluteString] : @"", 120 | @"loading" : @(_webView.loading), 121 | @"title": title, 122 | @"canGoBack": @([_webView canGoBack]), 123 | @"canGoForward" : @([_webView canGoForward]), 124 | }]; 125 | 126 | return event; 127 | } 128 | 129 | #pragma mark - UIWebViewDelegate methods 130 | 131 | static NSString *const RCTJSAJAXScheme = @"react-ajax"; 132 | static NSString *const RNBridgeScheme = @"react-message"; 133 | 134 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request 135 | navigationType:(UIWebViewNavigationType)navigationType 136 | { 137 | if ([request.URL.scheme isEqualToString:RNBridgeScheme]) { 138 | NSMutableDictionary *event = [self baseEvent]; 139 | [event addEntriesFromDictionary: @{ 140 | @"url": [request.URL absoluteString], 141 | @"navigationType": @(navigationType) 142 | }]; 143 | [_eventDispatcher sendInputEventWithName:RNBridgeableWebViewMessageSent body:event]; 144 | return false; 145 | } 146 | 147 | // We have this check to filter out iframe requests and whatnot 148 | BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; 149 | if (isTopFrame) { 150 | NSMutableDictionary *event = [self baseEvent]; 151 | [event addEntriesFromDictionary: @{ 152 | @"url": [request.URL absoluteString], 153 | @"navigationType": @(navigationType) 154 | }]; 155 | [_eventDispatcher sendInputEventWithName:@"topLoadingStart" body:event]; 156 | } 157 | 158 | // AJAX handler 159 | return ![request.URL.scheme isEqualToString:RCTJSAJAXScheme]; 160 | } 161 | 162 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error 163 | { 164 | if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { 165 | // NSURLErrorCancelled is reported when a page has a redirect OR if you load 166 | // a new URL in the WebView before the previous one came back. We can just 167 | // ignore these since they aren't real errors. 168 | // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os 169 | return; 170 | } 171 | 172 | NSMutableDictionary *event = [self baseEvent]; 173 | [event addEntriesFromDictionary: @{ 174 | @"domain": error.domain, 175 | @"code": @(error.code), 176 | @"description": [error localizedDescription], 177 | }]; 178 | [_eventDispatcher sendInputEventWithName:@"topLoadingError" body:event]; 179 | } 180 | 181 | - (void)webViewDidFinishLoad:(UIWebView *)webView 182 | { 183 | if (_shouldInjectAJAXHandler) { 184 | 185 | // From http://stackoverflow.com/questions/5353278/uiwebviewdelegate-not-monitoring-xmlhttprequest 186 | 187 | [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"\ 188 | var s_ajaxListener = new Object(); \n\ 189 | s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open; \n\ 190 | s_ajaxListener.tempSend = XMLHttpRequest.prototype.send; \n\ 191 | s_ajaxListener.callback = function() { \n\ 192 | window.location.href = '%@://' + this.url; \n\ 193 | } \n\ 194 | XMLHttpRequest.prototype.open = function(a,b) { \n\ 195 | s_ajaxListener.tempOpen.apply(this, arguments); \n\ 196 | s_ajaxListener.method = a; \n\ 197 | s_ajaxListener.url = b; \n\ 198 | if (a.toLowerCase() === 'get') { \n\ 199 | s_ajaxListener.data = (b.split('?'))[1]; \n\ 200 | } \n\ 201 | } \n\ 202 | XMLHttpRequest.prototype.send = function(a,b) { \n\ 203 | s_ajaxListener.tempSend.apply(this, arguments); \n\ 204 | if (s_ajaxListener.method.toLowerCase() === 'post') { \n\ 205 | s_ajaxListener.data = a; \n\ 206 | } \n\ 207 | s_ajaxListener.callback(); \n\ 208 | } \n\ 209 | ", RCTJSAJAXScheme]]; 210 | } 211 | 212 | // we only need the final 'finishLoad' call so only fire the event when we're actually done loading. 213 | if (!webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { 214 | [_eventDispatcher sendInputEventWithName:@"topLoadingFinish" body:[self baseEvent]]; 215 | } 216 | } 217 | 218 | @end 219 | -------------------------------------------------------------------------------- /RNBridgeableWebViewManager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "RCTViewManager.h" 11 | 12 | @interface RNBridgeableWebViewManager : RCTViewManager 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /RNBridgeableWebViewManager.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "RNBridgeableWebViewManager.h" 11 | 12 | #import "RCTBridge.h" 13 | #import "RCTSparseArray.h" 14 | #import "RCTUIManager.h" 15 | #import "RNBridgeableWebView.h" 16 | 17 | @implementation RNBridgeableWebViewManager 18 | 19 | RCT_EXPORT_MODULE() 20 | 21 | - (UIView *)view 22 | { 23 | return [[RNBridgeableWebView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; 24 | } 25 | 26 | RCT_REMAP_VIEW_PROPERTY(url, URL, NSURL); 27 | RCT_REMAP_VIEW_PROPERTY(html, HTML, NSString); 28 | RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL); 29 | RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL); 30 | RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets); 31 | RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL); 32 | RCT_EXPORT_VIEW_PROPERTY(shouldInjectAJAXHandler, BOOL); 33 | 34 | - (NSDictionary *)customDirectEventTypes 35 | { 36 | return @{ 37 | RNBridgeableWebViewMessageSent: @{ 38 | @"registrationName": @"onWebViewMessageSent" 39 | } 40 | }; 41 | } 42 | 43 | - (NSDictionary *)constantsToExport 44 | { 45 | return @{ 46 | @"NavigationType": @{ 47 | @"LinkClicked": @(UIWebViewNavigationTypeLinkClicked), 48 | @"FormSubmitted": @(UIWebViewNavigationTypeFormSubmitted), 49 | @"BackForward": @(UIWebViewNavigationTypeBackForward), 50 | @"Reload": @(UIWebViewNavigationTypeReload), 51 | @"FormResubmitted": @(UIWebViewNavigationTypeFormResubmitted), 52 | @"Other": @(UIWebViewNavigationTypeOther) 53 | }, 54 | }; 55 | } 56 | 57 | RCT_EXPORT_METHOD(goBack:(NSNumber *)reactTag) 58 | { 59 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { 60 | RNBridgeableWebView *view = viewRegistry[reactTag]; 61 | if (![view isKindOfClass:[RNBridgeableWebView class]]) { 62 | RCTLogError(@"Invalid view returned from registry, expecting RKWebView, got: %@", view); 63 | } 64 | [view goBack]; 65 | }]; 66 | } 67 | 68 | RCT_EXPORT_METHOD(goForward:(NSNumber *)reactTag) 69 | { 70 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { 71 | id view = viewRegistry[reactTag]; 72 | if (![view isKindOfClass:[RNBridgeableWebView class]]) { 73 | RCTLogError(@"Invalid view returned from registry, expecting RKWebView, got: %@", view); 74 | } 75 | [view goForward]; 76 | }]; 77 | } 78 | 79 | 80 | RCT_EXPORT_METHOD(reload:(NSNumber *)reactTag) 81 | { 82 | [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { 83 | RNBridgeableWebView *view = viewRegistry[reactTag]; 84 | if (![view isKindOfClass:[RNBridgeableWebView class]]) { 85 | RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view); 86 | } 87 | [view reload]; 88 | }]; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /RNBridgeableWebview.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E29EA2601B374B2000EE4B2B /* RNBridgeableWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = E29EA25D1B374B2000EE4B2B /* RNBridgeableWebView.m */; }; 11 | E29EA2611B374B2000EE4B2B /* RNBridgeableWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E29EA25F1B374B2000EE4B2B /* RNBridgeableWebViewManager.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | E29EA2341B33517A00EE4B2B /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 0; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | E29EA2361B33517A00EE4B2B /* libRNBridgeableWebview.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNBridgeableWebview.a; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | E29EA25C1B374B2000EE4B2B /* RNBridgeableWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBridgeableWebView.h; sourceTree = ""; }; 29 | E29EA25D1B374B2000EE4B2B /* RNBridgeableWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNBridgeableWebView.m; sourceTree = ""; }; 30 | E29EA25E1B374B2000EE4B2B /* RNBridgeableWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNBridgeableWebViewManager.h; sourceTree = ""; }; 31 | E29EA25F1B374B2000EE4B2B /* RNBridgeableWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNBridgeableWebViewManager.m; sourceTree = ""; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | E29EA2331B33517A00EE4B2B /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | E29EA22D1B33517A00EE4B2B = { 46 | isa = PBXGroup; 47 | children = ( 48 | E29EA25C1B374B2000EE4B2B /* RNBridgeableWebView.h */, 49 | E29EA25D1B374B2000EE4B2B /* RNBridgeableWebView.m */, 50 | E29EA25E1B374B2000EE4B2B /* RNBridgeableWebViewManager.h */, 51 | E29EA25F1B374B2000EE4B2B /* RNBridgeableWebViewManager.m */, 52 | E29EA2371B33517A00EE4B2B /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | E29EA2371B33517A00EE4B2B /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | E29EA2361B33517A00EE4B2B /* libRNBridgeableWebview.a */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | /* End PBXGroup section */ 65 | 66 | /* Begin PBXNativeTarget section */ 67 | E29EA2351B33517A00EE4B2B /* RNBridgeableWebview */ = { 68 | isa = PBXNativeTarget; 69 | buildConfigurationList = E29EA24A1B33517A00EE4B2B /* Build configuration list for PBXNativeTarget "RNBridgeableWebview" */; 70 | buildPhases = ( 71 | E29EA2321B33517A00EE4B2B /* Sources */, 72 | E29EA2331B33517A00EE4B2B /* Frameworks */, 73 | E29EA2341B33517A00EE4B2B /* CopyFiles */, 74 | ); 75 | buildRules = ( 76 | ); 77 | dependencies = ( 78 | ); 79 | name = RNBridgeableWebview; 80 | productName = RNBridgeableWebview; 81 | productReference = E29EA2361B33517A00EE4B2B /* libRNBridgeableWebview.a */; 82 | productType = "com.apple.product-type.library.static"; 83 | }; 84 | /* End PBXNativeTarget section */ 85 | 86 | /* Begin PBXProject section */ 87 | E29EA22E1B33517A00EE4B2B /* Project object */ = { 88 | isa = PBXProject; 89 | attributes = { 90 | LastUpgradeCheck = 0630; 91 | ORGANIZATIONNAME = "Tom Hastjarjanto"; 92 | TargetAttributes = { 93 | E29EA2351B33517A00EE4B2B = { 94 | CreatedOnToolsVersion = 6.3.2; 95 | }; 96 | }; 97 | }; 98 | buildConfigurationList = E29EA2311B33517A00EE4B2B /* Build configuration list for PBXProject "RNBridgeableWebview" */; 99 | compatibilityVersion = "Xcode 3.2"; 100 | developmentRegion = English; 101 | hasScannedForEncodings = 0; 102 | knownRegions = ( 103 | en, 104 | ); 105 | mainGroup = E29EA22D1B33517A00EE4B2B; 106 | productRefGroup = E29EA2371B33517A00EE4B2B /* Products */; 107 | projectDirPath = ""; 108 | projectRoot = ""; 109 | targets = ( 110 | E29EA2351B33517A00EE4B2B /* RNBridgeableWebview */, 111 | ); 112 | }; 113 | /* End PBXProject section */ 114 | 115 | /* Begin PBXSourcesBuildPhase section */ 116 | E29EA2321B33517A00EE4B2B /* Sources */ = { 117 | isa = PBXSourcesBuildPhase; 118 | buildActionMask = 2147483647; 119 | files = ( 120 | E29EA2601B374B2000EE4B2B /* RNBridgeableWebView.m in Sources */, 121 | E29EA2611B374B2000EE4B2B /* RNBridgeableWebViewManager.m in Sources */, 122 | ); 123 | runOnlyForDeploymentPostprocessing = 0; 124 | }; 125 | /* End PBXSourcesBuildPhase section */ 126 | 127 | /* Begin XCBuildConfiguration section */ 128 | E29EA2481B33517A00EE4B2B /* Debug */ = { 129 | isa = XCBuildConfiguration; 130 | buildSettings = { 131 | ALWAYS_SEARCH_USER_PATHS = NO; 132 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 133 | CLANG_CXX_LIBRARY = "libc++"; 134 | CLANG_ENABLE_MODULES = YES; 135 | CLANG_ENABLE_OBJC_ARC = YES; 136 | CLANG_WARN_BOOL_CONVERSION = YES; 137 | CLANG_WARN_CONSTANT_CONVERSION = YES; 138 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 139 | CLANG_WARN_EMPTY_BODY = YES; 140 | CLANG_WARN_ENUM_CONVERSION = YES; 141 | CLANG_WARN_INT_CONVERSION = YES; 142 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 143 | CLANG_WARN_UNREACHABLE_CODE = YES; 144 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 145 | COPY_PHASE_STRIP = NO; 146 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 147 | ENABLE_STRICT_OBJC_MSGSEND = YES; 148 | GCC_C_LANGUAGE_STANDARD = gnu99; 149 | GCC_DYNAMIC_NO_PIC = NO; 150 | GCC_NO_COMMON_BLOCKS = YES; 151 | GCC_OPTIMIZATION_LEVEL = 0; 152 | GCC_PREPROCESSOR_DEFINITIONS = ( 153 | "DEBUG=1", 154 | "$(inherited)", 155 | ); 156 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 157 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 158 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 159 | GCC_WARN_UNDECLARED_SELECTOR = YES; 160 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 161 | GCC_WARN_UNUSED_FUNCTION = YES; 162 | GCC_WARN_UNUSED_VARIABLE = YES; 163 | HEADER_SEARCH_PATHS = ( 164 | "$(inherited)", 165 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 166 | "$(SRCROOT)/../react-native/React/**", 167 | "$(SRCROOT)/node_modules/react-native/React/**", 168 | ); 169 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 170 | MTL_ENABLE_DEBUG_INFO = YES; 171 | ONLY_ACTIVE_ARCH = YES; 172 | SDKROOT = iphoneos; 173 | }; 174 | name = Debug; 175 | }; 176 | E29EA2491B33517A00EE4B2B /* Release */ = { 177 | isa = XCBuildConfiguration; 178 | buildSettings = { 179 | ALWAYS_SEARCH_USER_PATHS = NO; 180 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 181 | CLANG_CXX_LIBRARY = "libc++"; 182 | CLANG_ENABLE_MODULES = YES; 183 | CLANG_ENABLE_OBJC_ARC = YES; 184 | CLANG_WARN_BOOL_CONVERSION = YES; 185 | CLANG_WARN_CONSTANT_CONVERSION = YES; 186 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 187 | CLANG_WARN_EMPTY_BODY = YES; 188 | CLANG_WARN_ENUM_CONVERSION = YES; 189 | CLANG_WARN_INT_CONVERSION = YES; 190 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 191 | CLANG_WARN_UNREACHABLE_CODE = YES; 192 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 193 | COPY_PHASE_STRIP = NO; 194 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 195 | ENABLE_NS_ASSERTIONS = NO; 196 | ENABLE_STRICT_OBJC_MSGSEND = YES; 197 | GCC_C_LANGUAGE_STANDARD = gnu99; 198 | GCC_NO_COMMON_BLOCKS = YES; 199 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 200 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 201 | GCC_WARN_UNDECLARED_SELECTOR = YES; 202 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 203 | GCC_WARN_UNUSED_FUNCTION = YES; 204 | GCC_WARN_UNUSED_VARIABLE = YES; 205 | HEADER_SEARCH_PATHS = ( 206 | "$(inherited)", 207 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 208 | "$(SRCROOT)/../react-native/React/**", 209 | "$(SRCROOT)/node_modules/react-native/React/**", 210 | ); 211 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 212 | MTL_ENABLE_DEBUG_INFO = NO; 213 | SDKROOT = iphoneos; 214 | VALIDATE_PRODUCT = YES; 215 | }; 216 | name = Release; 217 | }; 218 | E29EA24B1B33517A00EE4B2B /* Debug */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | OTHER_LDFLAGS = "-ObjC"; 222 | PRODUCT_NAME = "$(TARGET_NAME)"; 223 | SKIP_INSTALL = YES; 224 | }; 225 | name = Debug; 226 | }; 227 | E29EA24C1B33517A00EE4B2B /* Release */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | OTHER_LDFLAGS = "-ObjC"; 231 | PRODUCT_NAME = "$(TARGET_NAME)"; 232 | SKIP_INSTALL = YES; 233 | }; 234 | name = Release; 235 | }; 236 | /* End XCBuildConfiguration section */ 237 | 238 | /* Begin XCConfigurationList section */ 239 | E29EA2311B33517A00EE4B2B /* Build configuration list for PBXProject "RNBridgeableWebview" */ = { 240 | isa = XCConfigurationList; 241 | buildConfigurations = ( 242 | E29EA2481B33517A00EE4B2B /* Debug */, 243 | E29EA2491B33517A00EE4B2B /* Release */, 244 | ); 245 | defaultConfigurationIsVisible = 0; 246 | defaultConfigurationName = Release; 247 | }; 248 | E29EA24A1B33517A00EE4B2B /* Build configuration list for PBXNativeTarget "RNBridgeableWebview" */ = { 249 | isa = XCConfigurationList; 250 | buildConfigurations = ( 251 | E29EA24B1B33517A00EE4B2B /* Debug */, 252 | E29EA24C1B33517A00EE4B2B /* Release */, 253 | ); 254 | defaultConfigurationIsVisible = 0; 255 | defaultConfigurationName = Release; 256 | }; 257 | /* End XCConfigurationList section */ 258 | }; 259 | rootObject = E29EA22E1B33517A00EE4B2B /* Project object */; 260 | } 261 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-bridgeable-webview", 3 | "version": "0.0.7", 4 | "description": "A react-native webview with bridge to react-native code", 5 | "main": "BridgeableWebView.ios.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Intellicode/react-native-bridgeable-webview.git" 12 | }, 13 | "author": "Tom Hastjarjanto", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/Intellicode/react-native-bridgeable-webview/issues" 17 | }, 18 | "homepage": "https://github.com/Intellicode/react-native-bridgeable-webview#readme", 19 | "devDependencies": { 20 | "react-native": "^0.6.0" 21 | }, 22 | "peerDependencies": { 23 | "react-native": "^0.6.0" 24 | } 25 | } 26 | --------------------------------------------------------------------------------