From af7f45f151e8a2a69ddd7da03e99da759ab3d73f Mon Sep 17 00:00:00 2001 From: Ali Najafizadeh Date: Mon, 8 Jun 2015 14:08:54 -0400 Subject: [PATCH] bring webview from core to plugin --- WebViewJavascriptBridge/WebViewEx.h | 27 ++ WebViewJavascriptBridge/WebViewEx.m | 205 ++++++++++++++ WebViewJavascriptBridge/WebViewExManager.h | 14 + WebViewJavascriptBridge/WebViewExManager.m | 82 ++++++ jsx/index.ios.js | 17 +- jsx/webviewex.js | 253 ++++++++++++++++++ webview.xcodeproj/project.pbxproj | 46 +++- .../UserInterfaceState.xcuserstate | Bin 10304 -> 14680 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 5 + 9 files changed, 626 insertions(+), 23 deletions(-) create mode 100644 WebViewJavascriptBridge/WebViewEx.h create mode 100644 WebViewJavascriptBridge/WebViewEx.m create mode 100644 WebViewJavascriptBridge/WebViewExManager.h create mode 100644 WebViewJavascriptBridge/WebViewExManager.m create mode 100644 jsx/webviewex.js create mode 100644 webview.xcodeproj/xcuserdata/ali.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist diff --git a/WebViewJavascriptBridge/WebViewEx.h b/WebViewJavascriptBridge/WebViewEx.h new file mode 100644 index 0000000..6122069 --- /dev/null +++ b/WebViewJavascriptBridge/WebViewEx.h @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTView.h" + +@class RCTEventDispatcher; + +@interface WebViewEx : RCTView + +@property (nonatomic, strong) NSURL *URL; +@property (nonatomic, assign) UIEdgeInsets contentInset; +@property (nonatomic, assign) BOOL shouldInjectAJAXHandler; +@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; + +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; + +- (void)goForward; +- (void)goBack; +- (void)reload; + +@end \ No newline at end of file diff --git a/WebViewJavascriptBridge/WebViewEx.m b/WebViewJavascriptBridge/WebViewEx.m new file mode 100644 index 0000000..ede495a --- /dev/null +++ b/WebViewJavascriptBridge/WebViewEx.m @@ -0,0 +1,205 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "WebViewEx.h" + +#import + +#import "RCTAutoInsetsProtocol.h" +#import "RCTEventDispatcher.h" +#import "RCTLog.h" +#import "RCTUtils.h" +#import "RCTView.h" +#import "UIView+React.h" + +@interface WebViewEx () + +@end + +@implementation WebViewEx +{ + RCTEventDispatcher *_eventDispatcher; + UIWebView *_webView; +} + +- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +{ + if ((self = [super initWithFrame:CGRectZero])) { + super.backgroundColor = [UIColor clearColor]; + _automaticallyAdjustContentInsets = YES; + _contentInset = UIEdgeInsetsZero; + _eventDispatcher = eventDispatcher; + _webView = [[UIWebView alloc] initWithFrame:self.bounds]; + _webView.delegate = self; + [self addSubview:_webView]; + } + return self; +} + +- (void)goForward +{ + [_webView goForward]; +} + +- (void)goBack +{ + [_webView goBack]; +} + +- (void)reload +{ + [_webView reload]; +} + +- (void)setURL:(NSURL *)URL +{ + // Because of the way React works, as pages redirect, we actually end up + // passing the redirect urls back here, so we ignore them if trying to load + // the same url. We'll expose a call to 'reload' to allow a user to load + // the existing page. + if ([URL isEqual:_webView.request.URL]) { + return; + } + if (!URL) { + // Clear the webview + [_webView loadHTMLString:nil baseURL:nil]; + return; + } + [_webView loadRequest:[NSURLRequest requestWithURL:URL]]; +} + +- (void)setHTML:(NSString *)HTML +{ + [_webView loadHTMLString:HTML baseURL:nil]; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + _webView.frame = self.bounds; + [RCTView autoAdjustInsetsForView:self + withScrollView:_webView.scrollView + updateOffset:YES]; +} + +- (void)setContentInset:(UIEdgeInsets)contentInset +{ + _contentInset = contentInset; + [RCTView autoAdjustInsetsForView:self + withScrollView:_webView.scrollView + updateOffset:NO]; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor +{ + CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor); + self.opaque = _webView.opaque = (alpha == 1.0); + _webView.backgroundColor = backgroundColor; +} + +- (UIColor *)backgroundColor +{ + return _webView.backgroundColor; +} + +- (NSMutableDictionary *)baseEvent +{ + NSURL *url = _webView.request.URL; + NSString *title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"]; + NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary: @{ + @"target": self.reactTag, + @"url": url ? [url absoluteString] : @"", + @"loading" : @(_webView.loading), + @"title": title, + @"canGoBack": @([_webView canGoBack]), + @"canGoForward" : @([_webView canGoForward]), + }]; + + return event; +} + +#pragma mark - UIWebViewDelegate methods + +static NSString *const RCTJSAJAXScheme = @"react-ajax"; + +- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request + navigationType:(UIWebViewNavigationType)navigationType +{ + // We have this check to filter out iframe requests and whatnot + BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; + if (isTopFrame) { + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"url": [request.URL absoluteString], + @"navigationType": @(navigationType) + }]; + [_eventDispatcher sendInputEventWithName:@"topLoadingStart" body:event]; + } + + // AJAX handler + return ![request.URL.scheme isEqualToString:RCTJSAJAXScheme]; +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error +{ + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { + // NSURLErrorCancelled is reported when a page has a redirect OR if you load + // a new URL in the WebView before the previous one came back. We can just + // ignore these since they aren't real errors. + // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os + return; + } + + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"domain": error.domain, + @"code": @(error.code), + @"description": [error localizedDescription], + }]; + [_eventDispatcher sendInputEventWithName:@"topLoadingError" body:event]; +} + +- (void)webViewDidFinishLoad:(UIWebView *)webView +{ + if (_shouldInjectAJAXHandler) { + + // From http://stackoverflow.com/questions/5353278/uiwebviewdelegate-not-monitoring-xmlhttprequest + + [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"\ + var s_ajaxListener = new Object(); \n\ + s_ajaxListener.tempOpen = XMLHttpRequest.prototype.open; \n\ + s_ajaxListener.tempSend = XMLHttpRequest.prototype.send; \n\ + s_ajaxListener.callback = function() { \n\ + window.location.href = '%@://' + this.url; \n\ + } \n\ + XMLHttpRequest.prototype.open = function(a,b) { \n\ + s_ajaxListener.tempOpen.apply(this, arguments); \n\ + s_ajaxListener.method = a; \n\ + s_ajaxListener.url = b; \n\ + if (a.toLowerCase() === 'get') { \n\ + s_ajaxListener.data = (b.split('?'))[1]; \n\ + } \n\ + } \n\ + XMLHttpRequest.prototype.send = function(a,b) { \n\ + s_ajaxListener.tempSend.apply(this, arguments); \n\ + if (s_ajaxListener.method.toLowerCase() === 'post') { \n\ + s_ajaxListener.data = a; \n\ + } \n\ + s_ajaxListener.callback(); \n\ + } \n\ + ", RCTJSAJAXScheme]]; + } + + // we only need the final 'finishLoad' call so only fire the event when we're actually done loading. + if (!webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { + [_eventDispatcher sendInputEventWithName:@"topLoadingFinish" body:[self baseEvent]]; + } +} + +@end \ No newline at end of file diff --git a/WebViewJavascriptBridge/WebViewExManager.h b/WebViewJavascriptBridge/WebViewExManager.h new file mode 100644 index 0000000..b4ddbc7 --- /dev/null +++ b/WebViewJavascriptBridge/WebViewExManager.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTViewManager.h" + +@interface WebViewExManager : RCTViewManager + +@end \ No newline at end of file diff --git a/WebViewJavascriptBridge/WebViewExManager.m b/WebViewJavascriptBridge/WebViewExManager.m new file mode 100644 index 0000000..40db627 --- /dev/null +++ b/WebViewJavascriptBridge/WebViewExManager.m @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "WebViewExManager.h" + +#import "RCTBridge.h" +#import "RCTSparseArray.h" +#import "RCTUIManager.h" +#import "RCTWebView.h" + +@implementation WebViewExManager + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + return [[RCTWebView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; +} + +RCT_REMAP_VIEW_PROPERTY(url, URL, NSURL); +RCT_REMAP_VIEW_PROPERTY(html, HTML, NSString); +RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL); +RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL); +RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets); +RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL); +RCT_EXPORT_VIEW_PROPERTY(shouldInjectAJAXHandler, BOOL); + +- (NSDictionary *)constantsToExport +{ + return @{ + @"NavigationType": @{ + @"LinkClicked": @(UIWebViewNavigationTypeLinkClicked), + @"FormSubmitted": @(UIWebViewNavigationTypeFormSubmitted), + @"BackForward": @(UIWebViewNavigationTypeBackForward), + @"Reload": @(UIWebViewNavigationTypeReload), + @"FormResubmitted": @(UIWebViewNavigationTypeFormResubmitted), + @"Other": @(UIWebViewNavigationTypeOther) + }, + }; +} + +RCT_EXPORT_METHOD(goBack:(NSNumber *)reactTag) +{ + [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { + RCTWebView *view = viewRegistry[reactTag]; + if (![view isKindOfClass:[RCTWebView class]]) { + RCTLogError(@"Invalid view returned from registry, expecting RKWebView, got: %@", view); + } + [view goBack]; + }]; +} + +RCT_EXPORT_METHOD(goForward:(NSNumber *)reactTag) +{ + [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { + id view = viewRegistry[reactTag]; + if (![view isKindOfClass:[RCTWebView class]]) { + RCTLogError(@"Invalid view returned from registry, expecting RKWebView, got: %@", view); + } + [view goForward]; + }]; +} + + +RCT_EXPORT_METHOD(reload:(NSNumber *)reactTag) +{ + [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 reload]; + }]; +} + +@end \ No newline at end of file diff --git a/jsx/index.ios.js b/jsx/index.ios.js index 6166612..fda42f2 100644 --- a/jsx/index.ios.js +++ b/jsx/index.ios.js @@ -5,28 +5,19 @@ 'use strict'; var React = require('react-native'); +var WebViewEx = require('./webviewex.js'); + var { AppRegistry, StyleSheet, Text, - View, + View } = React; var webview = React.createClass({ render: function() { return ( - - - Welcome to React Native! - - - To get started, edit index.ios.js - - - Press Cmd+R to reload,{'\n'} - Cmd+D or shake for dev menu - - + ); } }); diff --git a/jsx/webviewex.js b/jsx/webviewex.js new file mode 100644 index 0000000..60c7d17 --- /dev/null +++ b/jsx/webviewex.js @@ -0,0 +1,253 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule WebViewEx + * @flow + */ +'use strict'; + +var ActivityIndicatorIOS = require('ActivityIndicatorIOS'); +var EdgeInsetsPropType = require('EdgeInsetsPropType'); +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var View = require('View'); + +var invariant = require('invariant'); +var keyMirror = require('keyMirror'); +var requireNativeComponent = require('requireNativeComponent'); + +var PropTypes = React.PropTypes; +var WebViewExManager = require('NativeModules').WebViewExManager; + +var BGWASH = 'rgba(255,255,255,0.8)'; +var RCT_WEBVIEW_REF = 'webviewex'; + +var WebViewState = keyMirror({ + IDLE: null, + LOADING: null, + ERROR: null, +}); + +var NavigationType = { + click: WebViewExManager.NavigationType.LinkClicked, + formsubmit: WebViewExManager.NavigationType.FormSubmitted, + backforward: WebViewExManager.NavigationType.BackForward, + reload: WebViewExManager.NavigationType.Reload, + formresubmit: WebViewExManager.NavigationType.FormResubmitted, + other: WebViewExManager.NavigationType.Other, +}; + +type ErrorEvent = { + domain: any; + code: any; + description: any; +} + +type Event = Object; + +var defaultRenderLoading = () => ( + + + +); +var defaultRenderError = (errorDomain, errorCode, errorDesc) => ( + + + Error loading page + + + {'Domain: ' + errorDomain} + + + {'Error Code: ' + errorCode} + + + {'Description: ' + errorDesc} + + +); + +var WebViewEx = React.createClass({ + statics: { + NavigationType: NavigationType, + }, + + propTypes: { + url: PropTypes.string, + html: PropTypes.string, + renderError: PropTypes.func, // view to show if there's an error + renderLoading: PropTypes.func, // loading indicator to show + bounces: PropTypes.bool, + scrollEnabled: PropTypes.bool, + automaticallyAdjustContentInsets: PropTypes.bool, + shouldInjectAJAXHandler: PropTypes.bool, + contentInset: EdgeInsetsPropType, + onNavigationStateChange: PropTypes.func, + startInLoadingState: PropTypes.bool, // force WebViewEx to show loadingView on first load + style: View.propTypes.style, + /** + * Used for android only, JS is enabled by default for WebViewEx on iOS + */ + javaScriptEnabledAndroid: PropTypes.bool, + }, + + getInitialState: function() { + return { + viewState: WebViewState.IDLE, + lastErrorEvent: (null: ?ErrorEvent), + startInLoadingState: true, + }; + }, + + componentWillMount: function() { + if (this.props.startInLoadingState) { + this.setState({viewState: WebViewState.LOADING}); + } + }, + + render: function() { + var otherView = null; + + if (this.state.viewState === WebViewState.LOADING) { + otherView = (this.props.renderLoading || defaultRenderLoading)(); + } else if (this.state.viewState === WebViewState.ERROR) { + var errorEvent = this.state.lastErrorEvent; + invariant( + errorEvent != null, + 'lastErrorEvent expected to be non-null' + ); + otherView = (this.props.renderError || defaultRenderError)( + errorEvent.domain, + errorEvent.code, + errorEvent.description + ); + } else if (this.state.viewState !== WebViewState.IDLE) { + console.error( + 'WebViewEx invalid state encountered: ' + this.state.loading + ); + } + + var webViewStyles = [styles.container, styles.webView, this.props.style]; + if (this.state.viewState === WebViewState.LOADING || + this.state.viewState === WebViewState.ERROR) { + // if we're in either LOADING or ERROR states, don't show the webView + webViewStyles.push(styles.hidden); + } + + var webView = + ; + + return ( + + {webView} + {otherView} + + ); + }, + + goForward: function() { + WebViewExManager.goForward(this.getWebWiewHandle()); + }, + + goBack: function() { + WebViewExManager.goBack(this.getWebWiewHandle()); + }, + + reload: function() { + WebViewExManager.reload(this.getWebWiewHandle()); + }, + + /** + * We return an event with a bunch of fields including: + * url, title, loading, canGoBack, canGoForward + */ + updateNavigationState: function(event: Event) { + if (this.props.onNavigationStateChange) { + this.props.onNavigationStateChange(event.nativeEvent); + } + }, + + getWebWiewHandle: function(): any { + return React.findNodeHandle(this.refs[RCT_WEBVIEW_REF]); + }, + + onLoadingStart: function(event: Event) { + this.updateNavigationState(event); + }, + + onLoadingError: function(event: Event) { + event.persist(); // persist this event because we need to store it + console.error('Encountered an error loading page', event.nativeEvent); + + this.setState({ + lastErrorEvent: event.nativeEvent, + viewState: WebViewState.ERROR + }); + }, + + onLoadingFinish: function(event: Event) { + this.setState({ + viewState: WebViewState.IDLE, + }); + this.updateNavigationState(event); + }, +}); + +var WebViewEx = requireNativeComponent('WebViewEx', WebViewEx); + +var styles = StyleSheet.create({ + container: { + flex: 1, + }, + errorContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: BGWASH, + }, + errorText: { + fontSize: 14, + textAlign: 'center', + marginBottom: 2, + }, + errorTextTitle: { + fontSize: 15, + fontWeight: '500', + marginBottom: 10, + }, + hidden: { + height: 0, + flex: 0, // disable 'flex:1' when hiding a View + }, + loadingView: { + backgroundColor: BGWASH, + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + webView: { + backgroundColor: '#ffffff', + } +}); + +module.exports = WebViewEx; diff --git a/webview.xcodeproj/project.pbxproj b/webview.xcodeproj/project.pbxproj index 4fb28cd..1d6e85d 100644 --- a/webview.xcodeproj/project.pbxproj +++ b/webview.xcodeproj/project.pbxproj @@ -22,6 +22,9 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 41117F301B25EB7800342843 /* WebViewJavascriptBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 41117F2F1B25EB7800342843 /* WebViewJavascriptBridge.m */; }; + 41117F3D1B25ED7700342843 /* WebViewExManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 41117F3C1B25ED7700342843 /* WebViewExManager.m */; }; + 41117F401B260BBB00342843 /* WebViewEx.m in Sources */ = {isa = PBXBuildFile; fileRef = 41117F3F1B260BBB00342843 /* WebViewEx.m */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; /* End PBXBuildFile section */ @@ -107,16 +110,16 @@ /* Begin PBXFileReference section */ 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = iOS/main.jsbundle; sourceTree = ""; }; - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = ""; }; - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = node_modules/react-native/Libraries/Image/RCTImage.xcodeproj; sourceTree = ""; }; - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj; sourceTree = ""; }; - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; 00E356EE1AD99517003FC87E /* webviewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = webviewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* webviewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = webviewTests.m; sourceTree = ""; }; - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj; sourceTree = ""; }; - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = ""; }; + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* webview.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = webview.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = iOS/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = iOS/AppDelegate.m; sourceTree = ""; }; @@ -124,9 +127,15 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = iOS/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iOS/main.m; sourceTree = ""; }; - 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = node_modules/react-native/React/React.xcodeproj; sourceTree = ""; }; - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = node_modules/react-native/Libraries/Text/RCTText.xcodeproj; sourceTree = ""; }; + 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; + 41117F2E1B25EB7800342843 /* WebViewJavascriptBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewJavascriptBridge.h; sourceTree = ""; }; + 41117F2F1B25EB7800342843 /* WebViewJavascriptBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewJavascriptBridge.m; sourceTree = ""; }; + 41117F3B1B25ED7700342843 /* WebViewExManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewExManager.h; sourceTree = ""; }; + 41117F3C1B25ED7700342843 /* WebViewExManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewExManager.m; sourceTree = ""; }; + 41117F3E1B260BBB00342843 /* WebViewEx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebViewEx.h; sourceTree = ""; }; + 41117F3F1B260BBB00342843 /* WebViewEx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebViewEx.m; sourceTree = ""; }; + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -252,6 +261,19 @@ name = Products; sourceTree = ""; }; + 41117F2D1B25EB7800342843 /* WebViewJavascriptBridge */ = { + isa = PBXGroup; + children = ( + 41117F2E1B25EB7800342843 /* WebViewJavascriptBridge.h */, + 41117F2F1B25EB7800342843 /* WebViewJavascriptBridge.m */, + 41117F3B1B25ED7700342843 /* WebViewExManager.h */, + 41117F3C1B25ED7700342843 /* WebViewExManager.m */, + 41117F3E1B260BBB00342843 /* WebViewEx.h */, + 41117F3F1B260BBB00342843 /* WebViewEx.m */, + ); + path = WebViewJavascriptBridge; + sourceTree = ""; + }; 78C398B11ACF4ADC00677621 /* Products */ = { isa = PBXGroup; children = ( @@ -288,6 +310,7 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 41117F2D1B25EB7800342843 /* WebViewJavascriptBridge */, 13B07FAE1A68108700A75B9A /* webview */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 00E356EF1AD99517003FC87E /* webviewTests */, @@ -526,8 +549,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 41117F3D1B25ED7700342843 /* WebViewExManager.m in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 41117F301B25EB7800342843 /* WebViewJavascriptBridge.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, + 41117F401B260BBB00342843 /* WebViewEx.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/webview.xcodeproj/project.xcworkspace/xcuserdata/ali.xcuserdatad/UserInterfaceState.xcuserstate b/webview.xcodeproj/project.xcworkspace/xcuserdata/ali.xcuserdatad/UserInterfaceState.xcuserstate index 01b0fa209362dbb98d1699e14751fcbdfa64c0bd..5a2c3144f7a0209bc57a97da2245ba5c56f3c397 100644 GIT binary patch literal 14680 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 F{2!C|MzH_@ delta 5849 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 DDG<}K diff --git a/webview.xcodeproj/xcuserdata/ali.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/webview.xcodeproj/xcuserdata/ali.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..fe2b454 --- /dev/null +++ b/webview.xcodeproj/xcuserdata/ali.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + +