fix(iOS): UIWebView Removal (#828)

Apple required us to remove this (see #819)

BREAKING CHANGE: UIWebView has been removed
BREAKING CHANGE: useWebkit prop removal
BREAKING CHANGE: scalesPageToFit prop removal on iOS (since it's not compatible with WKWebview)
BREAKING CHANGE: Renamed RNCWKWebView to RNCWebView on iOS
This commit is contained in:
Thibault Malbranche 2019-08-30 12:04:32 +02:00 committed by GitHub
parent 3517d2db81
commit 8549be5fd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 163 additions and 815 deletions

View File

@ -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)
<a href="https://www.npmjs.com/package/react-native-webview"><img src="https://img.shields.io/npm/v/react-native-webview.svg"></a>
@ -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

View File

@ -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`

View File

@ -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.

View File

@ -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

View File

@ -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
<WebView
source={{ uri: 'https://facebook.github.io/react-native' }}
applicationNameForUserAgent={"DemoApp/1.1.0"}
applicationNameForUserAgent={'DemoApp/1.1.0'}
/>
// 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 |
| ------- | -------- | -------- |

View File

@ -1,39 +0,0 @@
#import <React/RCTView.h>
@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 <NSObject>
- (BOOL)webView:(RNCUIWebView *)webView
shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback;
@end
@interface RNCUIWebView : RCTView
@property (nonatomic, weak) id<RNCUIWebViewDelegate> 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

View File

@ -1,332 +0,0 @@
#import "RNCUIWebView.h"
// #import <UIKit/UIKit.h>
#import <React/RCTAutoInsetsProtocol.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
#import <React/RCTView.h>
#import <React/UIView+React.h>
NSString *const RNCJSNavigationScheme = @"react-js-navigation";
static NSString *const MessageHandlerName = @"ReactNativeWebView";
@interface RNCUIWebView () <UIWebViewDelegate, RCTAutoInsetsProtocol>
@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<NSString *, id> *)baseEvent
{
NSMutableDictionary<NSString *, id> *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<NSNumber *, NSString *> *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<NSString *, id> *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<NSString *, id> *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<NSString *, id> *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<NSString *, id> *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<NSString *, id> *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

View File

@ -1,5 +0,0 @@
#import <React/RCTViewManager.h>
@interface RNCUIWebViewManager : RCTViewManager
@end

View File

@ -1,154 +0,0 @@
#import "RNCUIWebViewManager.h"
#import <React/RCTBridge.h>
#import <React/RCTUIManager.h>
#import <React/UIView+React.h>
#import "RNCUIWebView.h"
@interface RNCUIWebViewManager () <RNCUIWebViewDelegate>
@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<NSNumber *, RNCUIWebView *> *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<NSNumber *, UIView *> *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<NSNumber *, RNCUIWebView *> *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<NSNumber *, RNCUIWebView *> *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<NSNumber *, RNCUIWebView *> *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<NSNumber *, RNCUIWebView *> *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<NSString *, id> *)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

View File

@ -9,19 +9,19 @@
#import <React/RCTDefines.h>
#import <WebKit/WebKit.h>
@class RNCWKWebView;
@class RNCWebView;
@protocol RNCWKWebViewDelegate <NSObject>
@protocol RNCWebViewDelegate <NSObject>
- (BOOL)webView:(RNCWKWebView *)webView
- (BOOL)webView:(RNCWebView *)webView
shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback;
@end
@interface RNCWKWebView : RCTView
@interface RNCWebView : RCTView
@property (nonatomic, weak) id<RNCWKWebViewDelegate> delegate;
@property (nonatomic, weak) id<RNCWebViewDelegate> delegate;
@property (nonatomic, copy) NSDictionary *source;
@property (nonatomic, assign) BOOL messagingEnabled;
@property (nonatomic, copy) NSString *injectedJavaScript;

View File

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNCWKWebView.h"
#import "RNCWebView.h"
#import <React/RCTConvert.h>
#import <React/RCTAutoInsetsProtocol.h>
#import "RNCWKProcessPoolManager.h"
@ -27,7 +27,7 @@ static NSURLCredential* clientAuthenticationCredential;
}
@end
@interface RNCWKWebView () <WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIScrollViewDelegate, RCTAutoInsetsProtocol>
@interface RNCWebView () <WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIScrollViewDelegate, RCTAutoInsetsProtocol>
@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;
}

View File

@ -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 = "<group>"; };
3515965F21A3C87E00623BFA /* RNCWKProcessPoolManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCWKProcessPoolManager.h; sourceTree = "<group>"; };
E914DBF2214474710071092B /* RNCUIWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCUIWebView.h; sourceTree = "<group>"; };
E914DBF3214474710071092B /* RNCUIWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCUIWebViewManager.m; sourceTree = "<group>"; };
E914DBF4214474710071092B /* RNCUIWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCUIWebView.m; sourceTree = "<group>"; };
E914DBF5214474710071092B /* RNCUIWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCUIWebViewManager.h; sourceTree = "<group>"; };
E91B351921446E6C00F9801F /* RNCWKWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCWKWebViewManager.h; sourceTree = "<group>"; };
E91B351A21446E6C00F9801F /* RNCWKWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCWKWebView.h; sourceTree = "<group>"; };
E91B351B21446E6C00F9801F /* RNCWKWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCWKWebViewManager.m; sourceTree = "<group>"; };
E91B351C21446E6C00F9801F /* RNCWKWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCWKWebView.m; sourceTree = "<group>"; };
E91B351921446E6C00F9801F /* RNCWebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCWebViewManager.h; sourceTree = "<group>"; };
E91B351A21446E6C00F9801F /* RNCWebView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCWebView.h; sourceTree = "<group>"; };
E91B351B21446E6C00F9801F /* RNCWebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCWebViewManager.m; sourceTree = "<group>"; };
E91B351C21446E6C00F9801F /* RNCWebView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCWebView.m; sourceTree = "<group>"; };
/* 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)";

View File

@ -7,5 +7,5 @@
#import <React/RCTViewManager.h>
@interface RNCWKWebViewManager : RCTViewManager
@interface RNCWebViewManager : RCTViewManager
@end

View File

@ -5,13 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/
#import "RNCWKWebViewManager.h"
#import "RNCWebViewManager.h"
#import <React/RCTUIManager.h>
#import <React/RCTDefines.h>
#import "RNCWKWebView.h"
#import "RNCWebView.h"
@interface RNCWKWebViewManager () <RNCWKWebViewDelegate>
@interface RNCWebViewManager () <RNCWebViewDelegate>
@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<NSNumber *, RNCWKWebView *> *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<NSNumber *, RNCWebView *> *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<NSNumber *, RNCWKWebView *> *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<NSNumber *, RNCWebView *> *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<NSNumber *, RNCWKWebView *> *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<NSNumber *, RNCWebView *> *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<NSNumber *, RNCWKWebView *> *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<NSNumber *, RNCWebView *> *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<NSNumber *, RNCWKWebView *> *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<NSNumber *, RNCWebView *> *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<NSNumber *, RNCWKWebView *> *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<NSNumber *, RNCWebView *> *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<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback
{

View File

@ -8,7 +8,7 @@
"Thibault Malbranche <malbranche.thibault@gmail.com>"
],
"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"
},

View File

@ -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<AndroidWebViewProps, State> {
webViewRef = React.createRef<NativeWebViewAndroid>();
getCommands = () => getViewManagerConfig('RNCWebView').Commands;
getCommands = () => UIManager.getViewManagerConfig('RNCWebView').Commands;
goForward = () => {
UIManager.dispatchViewManagerCommand(
@ -107,9 +106,9 @@ class WebView extends React.Component<AndroidWebViewProps, State> {
requestFocus = () => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
this.getCommands().requestFocus,
null,
this.getWebViewHandle(),
this.getCommands().requestFocus,
null,
);
};

View File

@ -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<IOSWebViewProps, State> {
static defaultProps = {
useWebKit: true,
javaScriptEnabled: true,
cacheEnabled: true,
originWhitelist: defaultOriginWhitelist,
@ -81,44 +72,8 @@ class WebView extends React.Component<IOSWebViewProps, State> {
webViewRef = React.createRef<NativeWebViewIOS>();
// 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<IOSWebViewProps, State> {
*/
requestFocus = () => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
this.getCommands().requestFocus,
null,
this.getWebViewHandle(),
this.getCommands().requestFocus,
null,
);
};
@ -285,32 +240,18 @@ class WebView extends React.Component<IOSWebViewProps, State> {
_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<IOSWebViewProps, State> {
originWhitelist,
renderError,
renderLoading,
scalesPageToFit = this.props.useWebKit ? undefined : true,
style,
useWebKit,
...otherProps
} = this.props;
@ -368,13 +307,9 @@ class WebView extends React.Component<IOSWebViewProps, State> {
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 = (
<NativeWebView
@ -390,7 +325,6 @@ class WebView extends React.Component<IOSWebViewProps, State> {
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}

View File

@ -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 = () => (
<View style={styles.loadingOrErrorView}>
<ActivityIndicator />
@ -95,7 +77,6 @@ const defaultRenderError = (
export {
defaultOriginWhitelist,
createOnShouldStartLoadWithRequest,
getViewManagerConfig,
defaultRenderLoading,
defaultRenderError,
};

View File

@ -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<WebViewNativeEvent>;
export type WebViewProgressEvent = NativeSyntheticEvent<WebViewNativeProgressEvent>;
export type WebViewProgressEvent = NativeSyntheticEvent<
WebViewNativeProgressEvent
>;
export type WebViewNavigationEvent = NativeSyntheticEvent<WebViewNavigation>;
@ -133,8 +129,8 @@ export type WebViewMessageEvent = NativeSyntheticEvent<WebViewMessage>;
export type WebViewErrorEvent = NativeSyntheticEvent<WebViewError>;
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<DataDetectorTypes>;
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`.