diff --git a/README.md b/README.md index bae98ff..ac2243a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # React Native WebView - a Modern, Cross-Platform WebView for React Native -[![star this repo](http://githubbadges.com/star.svg?user=react-native-community&repo=react-native-webview&style=flat)](https://github.com/react-native-community/react-native-webview) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) -[![All Contributors](https://img.shields.io/badge/all_contributors-16-orange.svg?style=flat-square)](#contributors) +[![star this repo](http://githubbadges.com/star.svg?user=react-native-community&repo=react-native-webview&style=flat)](https://github.com/react-native-community/react-native-webview) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) +[![All Contributors](https://img.shields.io/badge/all_contributors-16-orange.svg?style=flat-square)](#contributors) [![Known Vulnerabilities](https://snyk.io/test/github/react-native-community/react-native-webview/badge.svg?style=flat-square)](https://snyk.io/test/github/react-native-community/react-native-webview) @@ -17,7 +17,7 @@ _This project is maintained for free by these people using both their free time ## Platforms Supported -- [x] iOS (both UIWebView and WKWebView) +- [x] iOS - [x] Android _Note: Expo support for React Native WebView started with [Expo SDK v33.0.0](https://blog.expo.io/expo-sdk-v33-0-0-is-now-available-52d1c99dfe4c)._ @@ -34,10 +34,10 @@ This project follows [semantic versioning](https://semver.org/). We do not hesit Current Version: ![version](https://img.shields.io/npm/v/react-native-webview.svg) -yarn add react-native-webview@androidx +- [7.0.0](https://github.com/react-native-community/react-native-webview/releases/tag/v7.0.0 - Removed UIWebView + - [6.0.**2**](https://github.com/react-native-community/react-native-webview/releases/tag/v6.0.2) - Update to AndroidX. Make sure to enable it in your project's `android/gradle.properties`. See [Getting Started Guide](docs/Getting-Started.md). -yarn add react-native-webview - [5.0.**1**](https://github.com/react-native-community/react-native-webview/releases/tag/v5.0.0) - Refactored the old postMessage implementation for communication from webview to native. - [4.0.0](https://github.com/react-native-community/react-native-webview/releases/tag/v4.0.0) - Added cache (enabled by default). - [3.0.0](https://github.com/react-native-community/react-native-webview/releases/tag/v3.0.0) - WKWebview: Add shared process pool so cookies and localStorage are shared across webviews in iOS (enabled by default). @@ -45,7 +45,6 @@ yarn add react-native-webview **Upcoming:** -- UIWebView removal - this.webView.postMessage() removal (never documented and less flexible than injectJavascript) - Kotlin rewrite - Maybe Swift rewrite @@ -73,7 +72,7 @@ For more, read the [API Reference](./docs/Reference.md) and [Guide](./docs/Guide ## Common issues -- If you're getting `Invariant Violation: Native component for "RNCWKWebView does not exist"` it likely means you forgot to run `react-native link` or there was some error with the linking process +- If you're getting `Invariant Violation: Native component for "RNCWebView does not exist"` it likely means you forgot to run `react-native link` or there was some error with the linking process ## Contributing diff --git a/docs/Contributing.md b/docs/Contributing.md index b797ba1..2390e3a 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -45,5 +45,4 @@ $ yarn add ../react-native-webview && react-native link react-native-webview ## Notes - We use TypeScript. -- We don't intend to support UIWebView and will remove it soon. - After pulling this repo and installing all dependencies, you can run tests using the command: `yarn ci` diff --git a/docs/Custom-iOS.md b/docs/Custom-iOS.md index fcdfadb..3e84342 100644 --- a/docs/Custom-iOS.md +++ b/docs/Custom-iOS.md @@ -137,7 +137,7 @@ Once these are exposed, you can reference them in your custom web view class. If you open webpages that needs a Client Certificate for Authentication, you can create a credential and pass it to the webview: ``` -[RNCWKWebView setClientAuthenticationCredential:credential]; +[RNCWebView setClientAuthenticationCredential:credential]; ``` This can be paired with a call from Javascript to pass a string label for the certificate stored in keychain and use native calls to fetch the certificate to create a credential object. This call can be made anywhere that makes sense for your application (e.g. as part of the user authentication stack). The only requirement is to make this call before displaying any webviews. diff --git a/docs/Guide.md b/docs/Guide.md index ba82188..154758a 100644 --- a/docs/Guide.md +++ b/docs/Guide.md @@ -297,7 +297,7 @@ This runs the JavaScript in the `runFirst` string once the page is loaded. In th _Under the hood_ -> On iOS, `injectedJavaScript` runs a method on WKWebView called `evaluateJavaScript:completionHandler:` +> On iOS, `injectedJavaScript` runs a method on WebView called `evaluateJavaScript:completionHandler:` > On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback` #### The `injectJavaScript` method @@ -341,7 +341,7 @@ After 3 seconds, this code turns the background blue: _Under the hood_ -> On iOS, `injectJavaScript` calls WKWebView's `evaluateJS:andThen:` +> On iOS, `injectJavaScript` calls WebView's `evaluateJS:andThen:` > On Android, `injectJavaScript` calls Android WebView's `evaluateJavascriptWithFallback` method #### The `window.ReactNativeWebView.postMessage` method and `onMessage` prop diff --git a/docs/Reference.md b/docs/Reference.md index b5f770a..5d801e3 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -43,7 +43,6 @@ This document lays out the current public properties and methods for the React N - [`geolocationEnabled`](Reference.md#geolocationenabled) - [`allowUniversalAccessFromFileURLs`](Reference.md#allowUniversalAccessFromFileURLs) - [`allowingReadAccessToURL`](Reference.md#allowingReadAccessToURL) -- [`useWebKit`](Reference.md#usewebkit) - [`url`](Reference.md#url) - [`html`](Reference.md#html) - [`keyboardDisplayRequiresUserAction`](Reference.md#keyboardDisplayRequiresUserAction) @@ -132,7 +131,7 @@ const INJECTED_JAVASCRIPT = `(function() { source={{ uri: 'https://facebook.github.io/react-native' }} injectedJavaScript={INJECTED_JAVASCRIPT} onMessage={this.onMessage} -/> +/>; ``` --- @@ -312,7 +311,6 @@ Function that is invoked when the `WebView` is loading. > **_Note_** > -> On iOS, when useWebKit=false, this prop will not work. > On android, You can't get the url property, meaning that `event.nativeEvent.url` will be null. | Type | Required | @@ -459,11 +457,9 @@ Example: Boolean that controls whether the web content is scaled to fit the view and enables the user to change the scale. The default value is `true`. -On iOS, when [`useWebKit=true`](Reference.md#usewebkit), this prop will not work. - -| Type | Required | -| ---- | -------- | -| bool | No | +| Type | Required | Platform | +| ---- | -------- | -------- | +| bool | No | Android | --- @@ -605,26 +601,26 @@ Boolean value to enable third party cookies in the `WebView`. Used on Android Lo ### `userAgent` -Sets the user-agent for the `WebView`. This will only work for iOS if you are using WKWebView, not UIWebView (see https://developer.apple.com/documentation/webkit/wkwebview/1414950-customuseragent). +Sets the user-agent for the `WebView`. -| Type | Required | Platform | -| ------ | -------- | ---------------------- | -| string | No | Android, iOS WKWebView | +| Type | Required | +| ------ | -------- | +| string | No | --- ### `applicationNameForUserAgent` -Append to the existing user-agent. This will only work for iOS if you are using WKWebView, not UIWebView. Setting `userAgent` will override this. +Append to the existing user-agent. Setting `userAgent` will override this. -| Type | Required | Platform | -| ------ | -------- | ------------- | -| string | No | Android, iOS WKWebView | +| Type | Required | +| ------ | -------- | +| string | No | ```jsx // Resulting User-Agent will look like: // Mozilla/5.0 (Linux; Android 8.1.0; Android SDK built for x86 Build/OSM1.180201.021; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36 DemoApp/1.1.0 @@ -722,9 +718,6 @@ Possible values for `dataDetectorTypes` are: - `calendarEvent` - `none` - `all` - -With the [new WebKit](Reference.md#usewebkit) implementation, we have three new values: - - `trackingNumber` - `flightNumber` - `lookupSuggestion` @@ -798,21 +791,11 @@ Boolean that sets whether JavaScript running in the context of a file scheme URL ### `allowingReadAccessToURL` -A String value that indicates which URLs the WebView's file can then reference in scripts, AJAX requests, and CSS imports. This is only used in `RNCWKWebView` for WebViews that are loaded with a source.uri set to a `'file://'` URL. If not provided, the default is to only allow read access to the URL provided in source.uri itself. +A String value that indicates which URLs the WebView's file can then reference in scripts, AJAX requests, and CSS imports. This is only used in for WebViews that are loaded with a source.uri set to a `'file://'` URL. If not provided, the default is to only allow read access to the URL provided in source.uri itself. -| Type | Required | Platform | -| ------ | -------- | ------------- | -| string | No | iOS WKWebView | - ---- - -### `useWebKit` - -If true, use WKWebView instead of UIWebView. - -| Type | Required | Platform | -| ------- | -------- | -------- | -| boolean | No | iOS | +| Type | Required | Platform | +| ------ | -------- | -------- | +| string | No | iOS | --- @@ -838,7 +821,7 @@ If true, use WKWebView instead of UIWebView. ### `keyboardDisplayRequiresUserAction` -If false, web content can programmatically display the keyboard when using the WKWebView. The default value is `true`. +If false, web content can programmatically display the keyboard. The default value is `true`. | Type | Required | Platform | | ------- | -------- | -------- | @@ -848,7 +831,7 @@ If false, web content can programmatically display the keyboard when using the W ### `hideKeyboardAccessoryView` -If true, this will hide the keyboard accessory view (< > and Done) when using the WKWebView. +If true, this will hide the keyboard accessory view (< > and Done). | Type | Required | Platform | | ------- | -------- | -------- | @@ -858,7 +841,7 @@ If true, this will hide the keyboard accessory view (< > and Done) when using th ### `allowsBackForwardNavigationGestures` -If true, this will be able horizontal swipe gestures when using the WKWebView. The default value is `false`. +If true, this will be able horizontal swipe gestures. The default value is `false`. | Type | Required | Platform | | ------- | -------- | -------- | @@ -870,9 +853,9 @@ If true, this will be able horizontal swipe gestures when using the WKWebView. T Does not store any data within the lifetime of the WebView. -| Type | Required | Platform | -| ------- | -------- | ---------------------- | -| boolean | No | Android, iOS WKWebView | +| Type | Required | +| ------- | -------- | +| boolean | No | --- @@ -898,7 +881,7 @@ Sets whether the WebView should disable saving form data. The default value is ` ### `cacheEnabled` -Sets whether WebView & WKWebView should use browser caching. +Sets whether WebView should use browser caching. | Type | Required | Default | | ------- | -------- | ------- | @@ -928,7 +911,7 @@ A Boolean value that determines whether pressing on a link displays a preview of ### `sharedCookiesEnabled` -Set `true` if shared cookies from `[NSHTTPCookieStorage sharedHTTPCookieStorage]` should used for every load request in the `RNCWKWebView`. The default value is `false`. +Set `true` if shared cookies from `[NSHTTPCookieStorage sharedHTTPCookieStorage]` should used for every load request in the WebView. The default value is `false`. | Type | Required | Platform | | ------- | -------- | -------- | diff --git a/ios/RNCUIWebView.h b/ios/RNCUIWebView.h deleted file mode 100644 index c90a031..0000000 --- a/ios/RNCUIWebView.h +++ /dev/null @@ -1,39 +0,0 @@ -#import - -@class RNCUIWebView; - -/** - * Special scheme used to pass messages to the injectedJavaScript - * code without triggering a page load. Usage: - * - * window.location.href = RNCJSNavigationScheme + '://hello' - */ -extern NSString *const RNCJSNavigationScheme; - -@protocol RNCUIWebViewDelegate - -- (BOOL)webView:(RNCUIWebView *)webView -shouldStartLoadForRequest:(NSMutableDictionary *)request - withCallback:(RCTDirectEventBlock)callback; - -@end - -@interface RNCUIWebView : RCTView - -@property (nonatomic, weak) id delegate; - -@property (nonatomic, copy) NSDictionary *source; -@property (nonatomic, assign) UIEdgeInsets contentInset; -@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; -@property (nonatomic, assign) BOOL messagingEnabled; -@property (nonatomic, copy) NSString *injectedJavaScript; -@property (nonatomic, assign) BOOL scalesPageToFit; - -- (void)goForward; -- (void)goBack; -- (void)reload; -- (void)stopLoading; -- (void)postMessage:(NSString *)message; -- (void)injectJavaScript:(NSString *)script; - -@end diff --git a/ios/RNCUIWebView.m b/ios/RNCUIWebView.m deleted file mode 100644 index 95954b8..0000000 --- a/ios/RNCUIWebView.m +++ /dev/null @@ -1,332 +0,0 @@ -#import "RNCUIWebView.h" - -// #import -#import -#import -#import -#import -#import -#import -#import - -NSString *const RNCJSNavigationScheme = @"react-js-navigation"; - -static NSString *const MessageHandlerName = @"ReactNativeWebView"; - -@interface RNCUIWebView () - -@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; -@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; -@property (nonatomic, copy) RCTDirectEventBlock onLoadingError; -@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest; -@property (nonatomic, copy) RCTDirectEventBlock onMessage; - -@end - -@implementation RNCUIWebView -{ - UIWebView *_webView; - NSString *_injectedJavaScript; -} - -- (void)dealloc -{ - _webView.delegate = nil; -} - -- (instancetype)initWithFrame:(CGRect)frame -{ - if ((self = [super initWithFrame:frame])) { - super.backgroundColor = [UIColor clearColor]; - _automaticallyAdjustContentInsets = YES; - _contentInset = UIEdgeInsetsZero; - _webView = [[UIWebView alloc] initWithFrame:self.bounds]; - _webView.delegate = self; -#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */ - if ([_webView.scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) { - _webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; - } -#endif - [self addSubview:_webView]; - } - return self; -} - -RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - -- (void)goForward -{ - [_webView goForward]; -} - -- (void)goBack -{ - [_webView goBack]; -} - -- (void)reload -{ - NSURLRequest *request = [RCTConvert NSURLRequest:self.source]; - if (request.URL && !_webView.request.URL.absoluteString.length) { - [_webView loadRequest:request]; - } - else { - [_webView reload]; - } -} - -- (void)stopLoading -{ - [_webView stopLoading]; -} - -- (void)postMessage:(NSString *)message -{ - NSDictionary *eventInitDict = @{ - @"data": message, - }; - NSString *source = [NSString - stringWithFormat:@"window.dispatchEvent(new MessageEvent('message', %@));", - RCTJSONStringify(eventInitDict, NULL) - ]; - [_webView stringByEvaluatingJavaScriptFromString:source]; -} - -- (void)injectJavaScript:(NSString *)script -{ - [_webView stringByEvaluatingJavaScriptFromString:script]; -} - -- (void)setSource:(NSDictionary *)source -{ - if (![_source isEqualToDictionary:source]) { - _source = [source copy]; - - // Check for a static html source first - NSString *html = [RCTConvert NSString:source[@"html"]]; - if (html) { - NSURL *baseURL = [RCTConvert NSURL:source[@"baseUrl"]]; - if (!baseURL) { - baseURL = [NSURL URLWithString:@"about:blank"]; - } - [_webView loadHTMLString:html baseURL:baseURL]; - return; - } - - NSURLRequest *request = [RCTConvert NSURLRequest:source]; - // 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 ([request.URL isEqual:_webView.request.URL]) { - return; - } - if (!request.URL) { - // Clear the webview - [_webView loadHTMLString:@"" baseURL:nil]; - return; - } - [_webView loadRequest:request]; - } -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - _webView.frame = self.bounds; -} - -- (void)setContentInset:(UIEdgeInsets)contentInset -{ - _contentInset = contentInset; - [RCTView autoAdjustInsetsForView:self - withScrollView:_webView.scrollView - updateOffset:NO]; -} - -- (void)setScalesPageToFit:(BOOL)scalesPageToFit -{ - if (_webView.scalesPageToFit != scalesPageToFit) { - _webView.scalesPageToFit = scalesPageToFit; - [_webView reload]; - } -} - -- (BOOL)scalesPageToFit -{ - return _webView.scalesPageToFit; -} - -- (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 -{ - NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary:@{ - @"url": _webView.request.URL.absoluteString ?: @"", - @"loading" : @(_webView.loading), - @"title": [_webView stringByEvaluatingJavaScriptFromString:@"document.title"], - @"canGoBack": @(_webView.canGoBack), - @"canGoForward" : @(_webView.canGoForward), - }]; - - return event; -} - -- (void)refreshContentInset -{ - [RCTView autoAdjustInsetsForView:self - withScrollView:_webView.scrollView - updateOffset:YES]; -} - -#pragma mark - UIWebViewDelegate methods - -- (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request - navigationType:(UIWebViewNavigationType)navigationType -{ - BOOL isJSNavigation = [request.URL.scheme isEqualToString:RNCJSNavigationScheme]; - - static NSDictionary *navigationTypes; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - navigationTypes = @{ - @(UIWebViewNavigationTypeLinkClicked): @"click", - @(UIWebViewNavigationTypeFormSubmitted): @"formsubmit", - @(UIWebViewNavigationTypeBackForward): @"backforward", - @(UIWebViewNavigationTypeReload): @"reload", - @(UIWebViewNavigationTypeFormResubmitted): @"formresubmit", - @(UIWebViewNavigationTypeOther): @"other", - }; - }); - - // skip this for the JS Navigation handler - if (!isJSNavigation && _onShouldStartLoadWithRequest) { - NSMutableDictionary *event = [self baseEvent]; - [event addEntriesFromDictionary: @{ - @"url": (request.URL).absoluteString, - @"mainDocumentURL": (request.mainDocumentURL).absoluteString, - @"navigationType": navigationTypes[@(navigationType)] - }]; - if (![self.delegate webView:self - shouldStartLoadForRequest:event - withCallback:_onShouldStartLoadWithRequest]) { - return NO; - } - } - - if (_onLoadingStart) { - // 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": navigationTypes[@(navigationType)] - }]; - _onLoadingStart(event); - } - } - - if (isJSNavigation && [request.URL.host isEqualToString:MessageHandlerName]) { - NSString *data = request.URL.query; - data = [data stringByReplacingOccurrencesOfString:@"+" withString:@" "]; - data = [data stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - - NSMutableDictionary *event = [self baseEvent]; - [event addEntriesFromDictionary: @{ - @"data": data, - }]; - - NSString *source = [NSString stringWithFormat:@"window.%@.messageReceived();", MessageHandlerName]; - - [_webView stringByEvaluatingJavaScriptFromString:source]; - - _onMessage(event); - } - - // JS Navigation handler - return !isJSNavigation; -} - -- (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error -{ - if (_onLoadingError) { - 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; - } - - if ([error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 102) { - // Error code 102 "Frame load interrupted" is raised by the UIWebView if - // its delegate returns FALSE from webView:shouldStartLoadWithRequest:navigationType - // when the URL is from an http redirect. This is a common pattern when - // implementing OAuth with a WebView. - return; - } - - NSMutableDictionary *event = [self baseEvent]; - [event addEntriesFromDictionary:@{ - @"domain": error.domain, - @"code": @(error.code), - @"description": error.localizedDescription, - }]; - _onLoadingError(event); - } -} - -- (void)webViewDidFinishLoad:(UIWebView *)webView -{ - if (_messagingEnabled) { - NSString *source = [NSString stringWithFormat: - @"(function() {" - " var messageQueue = [];" - " var messagePending = false;" - - " function processQueue () {" - " if (!messageQueue.length || messagePending) return;" - " messagePending = true;" - " document.location = '%@://%@?' + encodeURIComponent(messageQueue.shift());" - " }" - - " window.%@ = {" - " postMessage: function (data) {" - " messageQueue.push(String(data));" - " processQueue();" - " }," - " messageReceived: function () {" - " messagePending = false;" - " processQueue();" - " }" - " };" - "})();", RNCJSNavigationScheme, MessageHandlerName, MessageHandlerName - ]; - [webView stringByEvaluatingJavaScriptFromString:source]; - } - if (_injectedJavaScript != nil) { - NSString *jsEvaluationValue = [webView stringByEvaluatingJavaScriptFromString:_injectedJavaScript]; - - NSMutableDictionary *event = [self baseEvent]; - event[@"jsEvaluationValue"] = jsEvaluationValue; - - _onLoadingFinish(event); - } - // we only need the final 'finishLoad' call so only fire the event when we're actually done loading. - else if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { - _onLoadingFinish([self baseEvent]); - } -} - -@end diff --git a/ios/RNCUIWebViewManager.h b/ios/RNCUIWebViewManager.h deleted file mode 100644 index 892d8c0..0000000 --- a/ios/RNCUIWebViewManager.h +++ /dev/null @@ -1,5 +0,0 @@ -#import - -@interface RNCUIWebViewManager : RCTViewManager - -@end diff --git a/ios/RNCUIWebViewManager.m b/ios/RNCUIWebViewManager.m deleted file mode 100644 index f78355a..0000000 --- a/ios/RNCUIWebViewManager.m +++ /dev/null @@ -1,154 +0,0 @@ -#import "RNCUIWebViewManager.h" - -#import -#import -#import -#import "RNCUIWebView.h" - -@interface RNCUIWebViewManager () - -@end - -@implementation RNCUIWebViewManager -{ - NSConditionLock *_shouldStartLoadLock; - BOOL _shouldStartLoad; -} - -RCT_EXPORT_MODULE() - -- (UIView *)view -{ - RNCUIWebView *webView = [RNCUIWebView new]; - webView.delegate = self; - return webView; -} - -RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary) -RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL) -RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL) -RCT_REMAP_VIEW_PROPERTY(decelerationRate, _webView.scrollView.decelerationRate, CGFloat) -RCT_EXPORT_VIEW_PROPERTY(scalesPageToFit, BOOL) -RCT_EXPORT_VIEW_PROPERTY(messagingEnabled, BOOL) -RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString) -RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets) -RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onMessage, RCTDirectEventBlock) -RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock) -RCT_REMAP_VIEW_PROPERTY(allowsInlineMediaPlayback, _webView.allowsInlineMediaPlayback, BOOL) -RCT_REMAP_VIEW_PROPERTY(mediaPlaybackRequiresUserAction, _webView.mediaPlaybackRequiresUserAction, BOOL) -RCT_REMAP_VIEW_PROPERTY(dataDetectorTypes, _webView.dataDetectorTypes, UIDataDetectorTypes) -RCT_REMAP_VIEW_PROPERTY(showsHorizontalScrollIndicator, _webView.scrollView.showsHorizontalScrollIndicator, BOOL) -RCT_REMAP_VIEW_PROPERTY(showsVerticalScrollIndicator, _webView.scrollView.showsVerticalScrollIndicator, BOOL) -RCT_REMAP_VIEW_PROPERTY(directionalLockEnabled, _webView.scrollView.directionalLockEnabled, BOOL) - -RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag) -{ - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCUIWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCUIWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCUIWebView, got: %@", view); - } else { - [view goBack]; - } - }]; -} - -RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag) -{ - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCUIWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCUIWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCUIWebView, got: %@", view); - } else { - [view goForward]; - } - }]; -} - -RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag) -{ - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCUIWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCUIWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCUIWebView, got: %@", view); - } else { - [view reload]; - } - }]; -} - -RCT_EXPORT_METHOD(stopLoading:(nonnull NSNumber *)reactTag) -{ - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCUIWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCUIWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCUIWebView, got: %@", view); - } else { - [view stopLoading]; - } - }]; -} - -RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber *)reactTag message:(NSString *)message) -{ - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCUIWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCUIWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCUIWebView, got: %@", view); - } else { - [view postMessage:message]; - } - }]; -} - -RCT_EXPORT_METHOD(injectJavaScript:(nonnull NSNumber *)reactTag script:(NSString *)script) -{ - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCUIWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCUIWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCUIWebView, got: %@", view); - } else { - [view injectJavaScript:script]; - } - }]; -} - -#pragma mark - Exported synchronous methods - -- (BOOL)webView:(__unused RNCUIWebView *)webView -shouldStartLoadForRequest:(NSMutableDictionary *)request - withCallback:(RCTDirectEventBlock)callback -{ - _shouldStartLoadLock = [[NSConditionLock alloc] initWithCondition:arc4random()]; - _shouldStartLoad = YES; - request[@"lockIdentifier"] = @(_shouldStartLoadLock.condition); - callback(request); - - // Block the main thread for a maximum of 250ms until the JS thread returns - if ([_shouldStartLoadLock lockWhenCondition:0 beforeDate:[NSDate dateWithTimeIntervalSinceNow:.25]]) { - BOOL returnValue = _shouldStartLoad; - [_shouldStartLoadLock unlock]; - _shouldStartLoadLock = nil; - return returnValue; - } else { - RCTLogWarn(@"Did not receive response to shouldStartLoad in time, defaulting to YES"); - return YES; - } -} - -RCT_EXPORT_METHOD(startLoadWithResult:(BOOL)result lockIdentifier:(NSInteger)lockIdentifier) -{ - if ([_shouldStartLoadLock tryLockWhenCondition:lockIdentifier]) { - _shouldStartLoad = result; - [_shouldStartLoadLock unlockWithCondition:0]; - } else { - RCTLogWarn(@"startLoadWithResult invoked with invalid lockIdentifier: " - "got %lld, expected %lld", (long long)lockIdentifier, (long long)_shouldStartLoadLock.condition); - } -} - -@end diff --git a/ios/RNCWKWebView.h b/ios/RNCWebView.h similarity index 91% rename from ios/RNCWKWebView.h rename to ios/RNCWebView.h index a2534f7..b2faebf 100644 --- a/ios/RNCWKWebView.h +++ b/ios/RNCWebView.h @@ -9,19 +9,19 @@ #import #import -@class RNCWKWebView; +@class RNCWebView; -@protocol RNCWKWebViewDelegate +@protocol RNCWebViewDelegate -- (BOOL)webView:(RNCWKWebView *)webView +- (BOOL)webView:(RNCWebView *)webView shouldStartLoadForRequest:(NSMutableDictionary *)request withCallback:(RCTDirectEventBlock)callback; @end -@interface RNCWKWebView : RCTView +@interface RNCWebView : RCTView -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id delegate; @property (nonatomic, copy) NSDictionary *source; @property (nonatomic, assign) BOOL messagingEnabled; @property (nonatomic, copy) NSString *injectedJavaScript; diff --git a/ios/RNCWKWebView.m b/ios/RNCWebView.m similarity index 99% rename from ios/RNCWKWebView.m rename to ios/RNCWebView.m index e7ace3d..98d23c1 100644 --- a/ios/RNCWKWebView.m +++ b/ios/RNCWebView.m @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNCWKWebView.h" +#import "RNCWebView.h" #import #import #import "RNCWKProcessPoolManager.h" @@ -27,7 +27,7 @@ static NSURLCredential* clientAuthenticationCredential; } @end -@interface RNCWKWebView () +@interface RNCWebView () @property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; @property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; @property (nonatomic, copy) RCTDirectEventBlock onLoadingError; @@ -38,12 +38,12 @@ static NSURLCredential* clientAuthenticationCredential; @property (nonatomic, copy) WKWebView *webView; @end -@implementation RNCWKWebView +@implementation RNCWebView { UIColor * _savedBackgroundColor; BOOL _savedHideKeyboardAccessoryView; BOOL _savedKeyboardDisplayRequiresUserAction; - + // Workaround for StatusBar appearance bug for iOS 12 // https://github.com/react-native-community/react-native-webview/issues/62 BOOL _isFullScreenVideoOpen; @@ -86,12 +86,12 @@ static NSURLCredential* clientAuthenticationCredential; addObserver:self selector:@selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:nil]; - + // Workaround for StatusBar appearance bug for iOS 12 // https://github.com/react-native-community/react-native-webview/issues/62 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleFullScreenVideoStatusBars) name:@"_MRMediaRemotePlayerSupportedCommandsDidChangeNotification" object:nil]; } - + return self; } @@ -449,22 +449,22 @@ static NSURLCredential* clientAuthenticationCredential; _savedKeyboardDisplayRequiresUserAction = keyboardDisplayRequiresUserAction; return; } - + if (_savedKeyboardDisplayRequiresUserAction == true) { return; } - + UIView* subview; - + for (UIView* view in _webView.scrollView.subviews) { if([[view.class description] hasPrefix:@"WK"]) subview = view; } - + if(subview == nil) return; - + Class class = subview.class; - + NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0}; NSOperatingSystemVersion iOS_12_2_0 = (NSOperatingSystemVersion){12, 2, 0}; NSOperatingSystemVersion iOS_13_0_0 = (NSOperatingSystemVersion){13, 0, 0}; @@ -507,7 +507,7 @@ static NSURLCredential* clientAuthenticationCredential; ((void (*)(id, SEL, void*, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3); }); } - + method_setImplementation(method, override); } @@ -623,7 +623,7 @@ static NSURLCredential* clientAuthenticationCredential; { [super layoutSubviews]; - // Ensure webview takes the position and dimensions of RNCWKWebView + // Ensure webview takes the position and dimensions of RNCWebView _webView.frame = self.bounds; _webView.scrollView.contentInset = _contentInset; } diff --git a/ios/RNCWebView.xcodeproj/project.pbxproj b/ios/RNCWebView.xcodeproj/project.pbxproj index f40ddb4..672e5e5 100644 --- a/ios/RNCWebView.xcodeproj/project.pbxproj +++ b/ios/RNCWebView.xcodeproj/project.pbxproj @@ -8,10 +8,8 @@ /* Begin PBXBuildFile section */ 3515965E21A3C86000623BFA /* RNCWKProcessPoolManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3515965D21A3C86000623BFA /* RNCWKProcessPoolManager.m */; }; - E914DBF6214474710071092B /* RNCUIWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E914DBF3214474710071092B /* RNCUIWebViewManager.m */; }; - E914DBF7214474710071092B /* RNCUIWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = E914DBF4214474710071092B /* RNCUIWebView.m */; }; - E91B351D21446E6C00F9801F /* RNCWKWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E91B351B21446E6C00F9801F /* RNCWKWebViewManager.m */; }; - E91B351E21446E6C00F9801F /* RNCWKWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = E91B351C21446E6C00F9801F /* RNCWKWebView.m */; }; + E91B351D21446E6C00F9801F /* RNCWebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E91B351B21446E6C00F9801F /* RNCWebViewManager.m */; }; + E91B351E21446E6C00F9801F /* RNCWebView.m in Sources */ = {isa = PBXBuildFile; fileRef = E91B351C21446E6C00F9801F /* RNCWebView.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,14 +28,10 @@ 134814201AA4EA6300B7C361 /* libRNCWebView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCWebView.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3515965D21A3C86000623BFA /* RNCWKProcessPoolManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCWKProcessPoolManager.m; sourceTree = ""; }; 3515965F21A3C87E00623BFA /* RNCWKProcessPoolManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCWKProcessPoolManager.h; sourceTree = ""; }; - E914DBF2214474710071092B /* RNCUIWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCUIWebView.h; sourceTree = ""; }; - E914DBF3214474710071092B /* RNCUIWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCUIWebViewManager.m; sourceTree = ""; }; - E914DBF4214474710071092B /* RNCUIWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCUIWebView.m; sourceTree = ""; }; - E914DBF5214474710071092B /* RNCUIWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCUIWebViewManager.h; sourceTree = ""; }; - E91B351921446E6C00F9801F /* RNCWKWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCWKWebViewManager.h; sourceTree = ""; }; - E91B351A21446E6C00F9801F /* RNCWKWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCWKWebView.h; sourceTree = ""; }; - E91B351B21446E6C00F9801F /* RNCWKWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCWKWebViewManager.m; sourceTree = ""; }; - E91B351C21446E6C00F9801F /* RNCWKWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCWKWebView.m; sourceTree = ""; }; + E91B351921446E6C00F9801F /* RNCWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCWebViewManager.h; sourceTree = ""; }; + E91B351A21446E6C00F9801F /* RNCWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCWebView.h; sourceTree = ""; }; + E91B351B21446E6C00F9801F /* RNCWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCWebViewManager.m; sourceTree = ""; }; + E91B351C21446E6C00F9801F /* RNCWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCWebView.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,14 +56,10 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - E914DBF2214474710071092B /* RNCUIWebView.h */, - E914DBF4214474710071092B /* RNCUIWebView.m */, - E914DBF5214474710071092B /* RNCUIWebViewManager.h */, - E914DBF3214474710071092B /* RNCUIWebViewManager.m */, - E91B351A21446E6C00F9801F /* RNCWKWebView.h */, - E91B351C21446E6C00F9801F /* RNCWKWebView.m */, - E91B351921446E6C00F9801F /* RNCWKWebViewManager.h */, - E91B351B21446E6C00F9801F /* RNCWKWebViewManager.m */, + E91B351A21446E6C00F9801F /* RNCWebView.h */, + E91B351C21446E6C00F9801F /* RNCWebView.m */, + E91B351921446E6C00F9801F /* RNCWebViewManager.h */, + E91B351B21446E6C00F9801F /* RNCWebViewManager.m */, 3515965D21A3C86000623BFA /* RNCWKProcessPoolManager.m */, 3515965F21A3C87E00623BFA /* RNCWKProcessPoolManager.h */, 134814211AA4EA7D00B7C361 /* Products */, @@ -112,7 +102,7 @@ }; buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCWebView" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -132,10 +122,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - E91B351D21446E6C00F9801F /* RNCWKWebViewManager.m in Sources */, - E914DBF7214474710071092B /* RNCUIWebView.m in Sources */, - E914DBF6214474710071092B /* RNCUIWebViewManager.m in Sources */, - E91B351E21446E6C00F9801F /* RNCWKWebView.m in Sources */, + E91B351D21446E6C00F9801F /* RNCWebViewManager.m in Sources */, + E91B351E21446E6C00F9801F /* RNCWebView.m in Sources */, 3515965E21A3C86000623BFA /* RNCWKProcessPoolManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -227,10 +215,10 @@ 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../node_modules/react-native/**"; HEADER_SEARCH_PATHS = ( "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../../React/**", + "$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../../react-native/React/**", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; @@ -243,10 +231,10 @@ 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../node_modules/react-native/**"; HEADER_SEARCH_PATHS = ( "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../../../React/**", + "$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../../react-native/React/**", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; diff --git a/ios/RNCWKWebViewManager.h b/ios/RNCWebViewManager.h similarity index 82% rename from ios/RNCWKWebViewManager.h rename to ios/RNCWebViewManager.h index 4382efa..383a921 100644 --- a/ios/RNCWKWebViewManager.h +++ b/ios/RNCWebViewManager.h @@ -7,5 +7,5 @@ #import -@interface RNCWKWebViewManager : RCTViewManager +@interface RNCWebViewManager : RCTViewManager @end diff --git a/ios/RNCWKWebViewManager.m b/ios/RNCWebViewManager.m similarity index 78% rename from ios/RNCWKWebViewManager.m rename to ios/RNCWebViewManager.m index 6c91278..5f823e6 100644 --- a/ios/RNCWKWebViewManager.m +++ b/ios/RNCWebViewManager.m @@ -5,13 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNCWKWebViewManager.h" +#import "RNCWebViewManager.h" #import #import -#import "RNCWKWebView.h" +#import "RNCWebView.h" -@interface RNCWKWebViewManager () +@interface RNCWebViewManager () @end @implementation RCTConvert (UIScrollView) @@ -27,7 +27,7 @@ RCT_ENUM_CONVERTER(UIScrollViewContentInsetAdjustmentBehavior, (@{ @end -@implementation RNCWKWebViewManager +@implementation RNCWebViewManager { NSConditionLock *_shouldStartLoadLock; BOOL _shouldStartLoad; @@ -37,7 +37,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - RNCWKWebView *webView = [RNCWKWebView new]; + RNCWebView *webView = [RNCWebView new]; webView.delegate = self; return webView; } @@ -80,58 +80,58 @@ RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock) RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber *)reactTag message:(NSString *)message) { - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCWKWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCWKWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCWKWebView, got: %@", view); + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RNCWebView *view = viewRegistry[reactTag]; + if (![view isKindOfClass:[RNCWebView class]]) { + RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view); } else { [view postMessage:message]; } }]; } -RCT_CUSTOM_VIEW_PROPERTY(bounces, BOOL, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(bounces, BOOL, RNCWebView) { view.bounces = json == nil ? true : [RCTConvert BOOL: json]; } -RCT_CUSTOM_VIEW_PROPERTY(useSharedProcessPool, BOOL, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(useSharedProcessPool, BOOL, RNCWebView) { view.useSharedProcessPool = json == nil ? true : [RCTConvert BOOL: json]; } -RCT_CUSTOM_VIEW_PROPERTY(scrollEnabled, BOOL, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(scrollEnabled, BOOL, RNCWebView) { view.scrollEnabled = json == nil ? true : [RCTConvert BOOL: json]; } -RCT_CUSTOM_VIEW_PROPERTY(sharedCookiesEnabled, BOOL, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(sharedCookiesEnabled, BOOL, RNCWebView) { view.sharedCookiesEnabled = json == nil ? false : [RCTConvert BOOL: json]; } -RCT_CUSTOM_VIEW_PROPERTY(decelerationRate, CGFloat, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(decelerationRate, CGFloat, RNCWebView) { view.decelerationRate = json == nil ? UIScrollViewDecelerationRateNormal : [RCTConvert CGFloat: json]; } -RCT_CUSTOM_VIEW_PROPERTY(directionalLockEnabled, BOOL, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(directionalLockEnabled, BOOL, RNCWebView) { view.directionalLockEnabled = json == nil ? true : [RCTConvert BOOL: json]; } -RCT_CUSTOM_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL, RNCWebView) { view.showsHorizontalScrollIndicator = json == nil ? true : [RCTConvert BOOL: json]; } -RCT_CUSTOM_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL, RNCWebView) { view.showsVerticalScrollIndicator = json == nil ? true : [RCTConvert BOOL: json]; } -RCT_CUSTOM_VIEW_PROPERTY(keyboardDisplayRequiresUserAction, BOOL, RNCWKWebView) { +RCT_CUSTOM_VIEW_PROPERTY(keyboardDisplayRequiresUserAction, BOOL, RNCWebView) { view.keyboardDisplayRequiresUserAction = json == nil ? true : [RCTConvert BOOL: json]; } RCT_EXPORT_METHOD(injectJavaScript:(nonnull NSNumber *)reactTag script:(NSString *)script) { - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCWKWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCWKWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCWKWebView, got: %@", view); + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RNCWebView *view = viewRegistry[reactTag]; + if (![view isKindOfClass:[RNCWebView class]]) { + RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view); } else { [view injectJavaScript:script]; } @@ -140,10 +140,10 @@ RCT_EXPORT_METHOD(injectJavaScript:(nonnull NSNumber *)reactTag script:(NSString RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag) { - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCWKWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCWKWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCWKWebView, got: %@", view); + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RNCWebView *view = viewRegistry[reactTag]; + if (![view isKindOfClass:[RNCWebView class]]) { + RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view); } else { [view goBack]; } @@ -152,10 +152,10 @@ RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag) RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag) { - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCWKWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCWKWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCWKWebView, got: %@", view); + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RNCWebView *view = viewRegistry[reactTag]; + if (![view isKindOfClass:[RNCWebView class]]) { + RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view); } else { [view goForward]; } @@ -164,10 +164,10 @@ RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag) RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag) { - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCWKWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCWKWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCWKWebView, got: %@", view); + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RNCWebView *view = viewRegistry[reactTag]; + if (![view isKindOfClass:[RNCWebView class]]) { + RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view); } else { [view reload]; } @@ -176,10 +176,10 @@ RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag) RCT_EXPORT_METHOD(stopLoading:(nonnull NSNumber *)reactTag) { - [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RNCWKWebView *view = viewRegistry[reactTag]; - if (![view isKindOfClass:[RNCWKWebView class]]) { - RCTLogError(@"Invalid view returned from registry, expecting RNCWKWebView, got: %@", view); + [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + RNCWebView *view = viewRegistry[reactTag]; + if (![view isKindOfClass:[RNCWebView class]]) { + RCTLogError(@"Invalid view returned from registry, expecting RNCWebView, got: %@", view); } else { [view stopLoading]; } @@ -188,7 +188,7 @@ RCT_EXPORT_METHOD(stopLoading:(nonnull NSNumber *)reactTag) #pragma mark - Exported synchronous methods -- (BOOL) webView:(RNCWKWebView *)webView +- (BOOL) webView:(RNCWebView *)webView shouldStartLoadForRequest:(NSMutableDictionary *)request withCallback:(RCTDirectEventBlock)callback { diff --git a/package.json b/package.json index 04dae08..9769b14 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "Thibault Malbranche " ], "license": "MIT", - "version": "6.11.1", + "version": "7.0.1", "homepage": "https://github.com/react-native-community/react-native-webview#readme", "scripts": { "ci": "CI=true && yarn lint && yarn test", @@ -20,7 +20,7 @@ }, "peerDependencies": { "react": "^16.0", - "react-native": ">=0.57 <0.60" + "react-native": ">=0.60 <0.62" }, "dependencies": { "escape-string-regexp": "1.0.5", @@ -48,7 +48,7 @@ "jest": "24.5.0", "metro-react-native-babel-preset": "0.53.1", "react": "16.8.3", - "react-native": "0.59.1", + "react-native": "0.60.5", "semantic-release": "15.10.3", "typescript": "3.3.3333" }, diff --git a/src/WebView.android.tsx b/src/WebView.android.tsx index 4196063..a880553 100644 --- a/src/WebView.android.tsx +++ b/src/WebView.android.tsx @@ -15,7 +15,6 @@ import invariant from 'invariant'; import { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, - getViewManagerConfig, defaultRenderError, defaultRenderLoading, } from './WebViewShared'; @@ -68,7 +67,7 @@ class WebView extends React.Component { webViewRef = React.createRef(); - getCommands = () => getViewManagerConfig('RNCWebView').Commands; + getCommands = () => UIManager.getViewManagerConfig('RNCWebView').Commands; goForward = () => { UIManager.dispatchViewManagerCommand( @@ -107,9 +106,9 @@ class WebView extends React.Component { requestFocus = () => { UIManager.dispatchViewManagerCommand( - this.getWebViewHandle(), - this.getCommands().requestFocus, - null, + this.getWebViewHandle(), + this.getCommands().requestFocus, + null, ); }; diff --git a/src/WebView.ios.tsx b/src/WebView.ios.tsx index c0f0ae2..0e69de6 100644 --- a/src/WebView.ios.tsx +++ b/src/WebView.ios.tsx @@ -13,7 +13,6 @@ import invariant from 'invariant'; import { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, - getViewManagerConfig, defaultRenderError, defaultRenderLoading, } from './WebViewShared'; @@ -28,7 +27,6 @@ import { ViewManager, State, CustomUIManager, - WebViewNativeConfig, } from './WebViewTypes'; import styles from './WebView.styles'; @@ -36,8 +34,6 @@ import styles from './WebView.styles'; const UIManager = NotTypedUIManager as CustomUIManager; const { resolveAssetSource } = Image; -let didWarnAboutUIWebViewUsage = false; -// Imported from https://github.com/facebook/react-native/blob/master/Libraries/Components/ScrollView/processDecelerationRate.js const processDecelerationRate = ( decelerationRate: DecelerationRateConstant | number | undefined, ) => { @@ -50,19 +46,14 @@ const processDecelerationRate = ( return newDecelerationRate; }; -const RNCUIWebViewManager = NativeModules.RNCUIWebViewManager as ViewManager; -const RNCWKWebViewManager = NativeModules.RNCWKWebViewManager as ViewManager; +const RNCWebViewManager = NativeModules.RNCWebViewManager as ViewManager; -const RNCUIWebView: typeof NativeWebViewIOS = requireNativeComponent( - 'RNCUIWebView', -); -const RNCWKWebView: typeof NativeWebViewIOS = requireNativeComponent( - 'RNCWKWebView', +const RNCWebView: typeof NativeWebViewIOS = requireNativeComponent( + 'RNCWebView', ); class WebView extends React.Component { static defaultProps = { - useWebKit: true, javaScriptEnabled: true, cacheEnabled: true, originWhitelist: defaultOriginWhitelist, @@ -81,44 +72,8 @@ class WebView extends React.Component { webViewRef = React.createRef(); - // eslint-disable-next-line camelcase - UNSAFE_componentWillMount() { - if (!this.props.useWebKit && !didWarnAboutUIWebViewUsage) { - didWarnAboutUIWebViewUsage = true; - console.warn( - 'UIWebView is deprecated and will be removed soon, please use WKWebView (do not override useWebkit={true} prop),' - + ' more infos here: https://github.com/react-native-community/react-native-webview/issues/312', - ); - } - if ( - this.props.useWebKit === true - && this.props.scalesPageToFit !== undefined - ) { - console.warn( - 'The scalesPageToFit property is not supported when useWebKit = true', - ); - } - if ( - !this.props.useWebKit - && this.props.allowsBackForwardNavigationGestures - ) { - console.warn( - 'The allowsBackForwardNavigationGestures property is not supported when useWebKit = false', - ); - } - - if (!this.props.useWebKit && this.props.incognito) { - console.warn( - 'The incognito property is not supported when useWebKit = false', - ); - } - } - // eslint-disable-next-line react/sort-comp - getCommands = () => - !this.props.useWebKit - ? getViewManagerConfig('RNCUIWebView').Commands - : getViewManagerConfig('RNCWKWebView').Commands; + getCommands = () => UIManager.getViewManagerConfig('RNCWebView').Commands; /** * Go forward one page in the web view's history. @@ -170,9 +125,9 @@ class WebView extends React.Component { */ requestFocus = () => { UIManager.dispatchViewManagerCommand( - this.getWebViewHandle(), - this.getCommands().requestFocus, - null, + this.getWebViewHandle(), + this.getCommands().requestFocus, + null, ); }; @@ -285,32 +240,18 @@ class WebView extends React.Component { _url: string, lockIdentifier: number, ) => { - let { viewManager }: WebViewNativeConfig = this.props.nativeConfig || {}; + const viewManager + = (this.props.nativeConfig && this.props.nativeConfig.viewManager) + || RNCWebViewManager; - if (this.props.useWebKit) { - viewManager = viewManager || RNCWKWebViewManager; - } else { - viewManager = viewManager || RNCUIWebViewManager; - } - invariant(viewManager != null, 'viewManager expected to be non-null'); viewManager.startLoadWithResult(!!shouldStart, lockIdentifier); }; componentDidUpdate(prevProps: IOSWebViewProps) { - if (!(prevProps.useWebKit && this.props.useWebKit)) { - return; - } - this.showRedboxOnPropChanges(prevProps, 'allowsInlineMediaPlayback'); this.showRedboxOnPropChanges(prevProps, 'incognito'); this.showRedboxOnPropChanges(prevProps, 'mediaPlaybackRequiresUserAction'); this.showRedboxOnPropChanges(prevProps, 'dataDetectorTypes'); - - if (this.props.scalesPageToFit !== undefined) { - console.warn( - 'The scalesPageToFit property is not supported when useWebKit = true', - ); - } } showRedboxOnPropChanges( @@ -333,9 +274,7 @@ class WebView extends React.Component { originWhitelist, renderError, renderLoading, - scalesPageToFit = this.props.useWebKit ? undefined : true, style, - useWebKit, ...otherProps } = this.props; @@ -368,13 +307,9 @@ class WebView extends React.Component { const decelerationRate = processDecelerationRate(decelerationRateProp); - let NativeWebView = nativeConfig.component as typeof NativeWebViewIOS; - - if (useWebKit) { - NativeWebView = NativeWebView || RNCWKWebView; - } else { - NativeWebView = NativeWebView || RNCUIWebView; - } + const NativeWebView + = (nativeConfig.component as typeof NativeWebViewIOS | undefined) + || RNCWebView; const webView = ( { onScroll={this.props.onScroll} onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} ref={this.webViewRef} - scalesPageToFit={scalesPageToFit} // TODO: find a better way to type this. source={resolveAssetSource(this.props.source as ImageSourcePropType)} style={webViewStyles} diff --git a/src/WebViewShared.tsx b/src/WebViewShared.tsx index f8f4d35..83771ca 100644 --- a/src/WebViewShared.tsx +++ b/src/WebViewShared.tsx @@ -1,21 +1,12 @@ import escapeStringRegexp from 'escape-string-regexp'; import React from 'react'; -import { - Linking, - UIManager as NotTypedUIManager, - View, - ActivityIndicator, - Text, -} from 'react-native'; +import { Linking, View, ActivityIndicator, Text } from 'react-native'; import { WebViewNavigationEvent, OnShouldStartLoadWithRequest, - CustomUIManager, } from './WebViewTypes'; import styles from './WebView.styles'; -const UIManager = NotTypedUIManager as CustomUIManager; - const defaultOriginWhitelist = ['http://*', 'https://*']; const extractOrigin = (url: string): string => { @@ -65,15 +56,6 @@ const createOnShouldStartLoadWithRequest = ( }; }; -const getViewManagerConfig = ( - viewManagerName: 'RNCUIWebView' | 'RNCWKWebView' | 'RNCWebView', -) => { - if (!UIManager.getViewManagerConfig) { - return UIManager[viewManagerName]; - } - return UIManager.getViewManagerConfig(viewManagerName); -}; - const defaultRenderLoading = () => ( @@ -95,7 +77,6 @@ const defaultRenderError = ( export { defaultOriginWhitelist, createOnShouldStartLoadWithRequest, - getViewManagerConfig, defaultRenderLoading, defaultRenderError, }; diff --git a/src/WebViewTypes.ts b/src/WebViewTypes.ts index 35be40b..7bd361f 100644 --- a/src/WebViewTypes.ts +++ b/src/WebViewTypes.ts @@ -22,7 +22,7 @@ export interface WebViewCommands { } export interface CustomUIManager extends UIManagerStatic { - getViewManagerConfig?: ( + getViewManagerConfig: ( name: string, ) => { Commands: WebViewCommands; @@ -32,12 +32,6 @@ export interface CustomUIManager extends UIManagerStatic { command: Function, params: object | null, ) => void; - RNCUIWebView: { - Commands: WebViewCommands; - }; - RNCWKWebView: { - Commands: WebViewCommands; - }; RNCWebView: { Commands: WebViewCommands; }; @@ -125,7 +119,9 @@ export interface WebViewError extends WebViewNativeEvent { export type WebViewEvent = NativeSyntheticEvent; -export type WebViewProgressEvent = NativeSyntheticEvent; +export type WebViewProgressEvent = NativeSyntheticEvent< + WebViewNativeProgressEvent +>; export type WebViewNavigationEvent = NativeSyntheticEvent; @@ -133,8 +129,8 @@ export type WebViewMessageEvent = NativeSyntheticEvent; export type WebViewErrorEvent = NativeSyntheticEvent; -export type DataDetectorTypes - = | 'phoneNumber' +export type DataDetectorTypes = + | 'phoneNumber' | 'link' | 'address' | 'calendarEvent' @@ -224,7 +220,6 @@ export interface CommonNativeWebViewProps extends ViewProps { onLoadingStart: (event: WebViewNavigationEvent) => void; onMessage: (event: WebViewMessageEvent) => void; onShouldStartLoadWithRequest: (event: WebViewNavigationEvent) => void; - scalesPageToFit?: boolean; showsHorizontalScrollIndicator?: boolean; showsVerticalScrollIndicator?: boolean; // TODO: find a better way to type this. @@ -239,6 +234,7 @@ export interface CommonNativeWebViewProps extends ViewProps { export interface AndroidNativeWebViewProps extends CommonNativeWebViewProps { allowFileAccess?: boolean; + scalesPageToFit?: boolean; allowUniversalAccessFromFileURLs?: boolean; androidHardwareAccelerationDisabled?: boolean; domStorageEnabled?: boolean; @@ -261,7 +257,11 @@ export interface IOSNativeWebViewProps extends CommonNativeWebViewProps { automaticallyAdjustContentInsets?: boolean; bounces?: boolean; contentInset?: ContentInsetProp; - contentInsetAdjustmentBehavior?: 'automatic'| 'scrollableAxes' | 'never' | 'always'; + contentInsetAdjustmentBehavior?: + | 'automatic' + | 'scrollableAxes' + | 'never' + | 'always'; dataDetectorTypes?: DataDetectorTypes | ReadonlyArray; decelerationRate?: number; directionalLockEnabled?: boolean; @@ -272,12 +272,6 @@ export interface IOSNativeWebViewProps extends CommonNativeWebViewProps { } export interface IOSWebViewProps extends WebViewSharedProps { - /** - * If true, use WKWebView instead of UIWebView. - * @platform ios - */ - useWebKit?: boolean; - /** * Does not store any data within the lifetime of the WebView. */ @@ -331,7 +325,11 @@ export interface IOSWebViewProps extends WebViewSharedProps { * content area of the scroll view. The default value of this property is * "never". Available on iOS 11 and later. */ - contentInsetAdjustmentBehavior?: 'automatic'| 'scrollableAxes' | 'never' | 'always' + contentInsetAdjustmentBehavior?: + | 'automatic' + | 'scrollableAxes' + | 'never' + | 'always'; /** * The amount by which the web view content is inset from the edges of @@ -408,8 +406,8 @@ export interface IOSWebViewProps extends WebViewSharedProps { allowsLinkPreview?: boolean; /** - * Set true if shared cookies from HTTPCookieStorage should used for every load request in the - * `RNCWKWebView`. The default value is `false`. + * Set true if shared cookies from HTTPCookieStorage should used for every load request. + * The default value is `false`. * @platform ios */ sharedCookiesEnabled?: boolean; @@ -437,9 +435,8 @@ export interface IOSWebViewProps extends WebViewSharedProps { /** * A String value that indicates which URLs the WebView's file can then * reference in scripts, AJAX requests, and CSS imports. This is only used - * in `RNCWKWebView` for WebViews that are loaded with a source.uri set to a - * `'file://'` URL. - * + * for WebViews that are loaded with a source.uri set to a `'file://'` URL. + * * If not provided, the default is to only allow read access to the URL * provided in source.uri itself. * @platform ios @@ -463,6 +460,13 @@ export interface AndroidWebViewProps extends WebViewSharedProps { */ overScrollMode?: OverScrollModeType; + /** + * Boolean that controls whether the web content is scaled to fit + * the view and enables the user to change the scale. The default value + * is `true`. + */ + scalesPageToFit?: boolean; + /** * Sets whether Geolocation is enabled. The default is false. * @platform android @@ -543,10 +547,10 @@ export interface AndroidWebViewProps extends WebViewSharedProps { * @platform android */ mixedContentMode?: 'never' | 'always' | 'compatibility'; - + /** * Sets ability to open fullscreen videos on Android devices. - */ + */ allowsFullscreenVideo?: boolean; } @@ -562,7 +566,7 @@ export interface WebViewSharedProps extends ViewProps { * @platform android */ javaScriptEnabled?: boolean; - + /** * Function that returns a view to show if there's an error. */ @@ -645,15 +649,6 @@ export interface WebViewSharedProps extends ViewProps { */ showsVerticalScrollIndicator?: boolean; - /** - * Boolean that controls whether the web content is scaled to fit - * the view and enables the user to change the scale. The default value - * is `true`. - * - * On iOS, when `useWebKit=true`, this prop will not work. - */ - scalesPageToFit?: boolean; - /** * Boolean that determines whether HTML5 audio and video requires the user * to tap them before they start playing. The default value is `true`.