react-native-webview/ios/RNCWKWebView.m

679 lines
23 KiB
Mathematica
Raw Normal View History

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
2018-09-18 16:32:16 +00:00
#import "RNCWKWebView.h"
#import <React/RCTConvert.h>
#import <React/RCTAutoInsetsProtocol.h>
#import "RNCWKProcessPoolManager.h"
#import <UIKit/UIKit.h>
#import "objc/runtime.h"
static NSTimer *keyboardTimer;
static NSString *const MessageHandlerName = @"ReactNativeWebView";
static NSURLCredential* clientAuthenticationCredential;
// runtime trick to remove WKWebView keyboard default toolbar
// see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279
@interface _SwizzleHelperWK : NSObject @end
@implementation _SwizzleHelperWK
-(id)inputAccessoryView
{
return nil;
}
@end
2018-09-18 16:32:16 +00:00
@interface RNCWKWebView () <WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIScrollViewDelegate, RCTAutoInsetsProtocol>
@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingProgress;
@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
@property (nonatomic, copy) RCTDirectEventBlock onMessage;
@property (nonatomic, copy) WKWebView *webView;
@end
2018-09-18 16:32:16 +00:00
@implementation RNCWKWebView
{
UIColor * _savedBackgroundColor;
BOOL _savedHideKeyboardAccessoryView;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
super.backgroundColor = [UIColor clearColor];
_bounces = YES;
_scrollEnabled = YES;
_showsHorizontalScrollIndicator = YES;
_showsVerticalScrollIndicator = YES;
_directionalLockEnabled = YES;
_automaticallyAdjustContentInsets = YES;
_contentInset = UIEdgeInsetsZero;
}
// Workaround for a keyboard dismissal bug present in iOS 12
// https://openradar.appspot.com/radar?id=5018321736957952
if (@available(iOS 12.0, *)) {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide)
name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow)
name:UIKeyboardWillShowNotification object:nil];
}
return self;
}
fix(WKWebView): resolved crash with WebKit on iOS 9.x. (#342) This PR fixes two crashes when enableWebKit is true (which was the default) on iOS 9.x. The first one when the WebView component was mounted (fixes issue #150) and the second one, when the component was unmounted (fixes issue #213). The first problem happen when the RNCWKWebView.m was loaded: The programming guide for WebKit (version 2006) ([pdf](http://mirror.informatimago.com/next/developer.apple.com/documentation/Cocoa/Conceptual/DisplayWebContent/WebKit_DisplayWebContent.pdf)) from Apple said, that it is (was) required to check if the 'new' WebKit Framework was available. This was required when the WebKit framework was only available on Mac OS X (10.2) when the Safari was installed. 😆 Because WebKit is (currently..) a fix part of iOS we didn't need this check this anymore to determinate if WebKit is available. I also see no other reference that this is required in iOS so I just remove this code. Without this code the WebView works also on iOS 9.x. The second issue happen when the WKWebView was removed from the native view hierarchy. Its required (only on iOS 9) to remove the UIScrollView delegate before cleaning up the webview. Its possible to try this PR with: ``` npm install --save "react-native-webview@jerolimov/react-native-webview#fix-ios9-crash" # or yarn add "react-native-webview@jerolimov/react-native-webview#fix-ios9-crash" ``` If you use CocoaPods, you should also update your Pods with ``` cd ios && pod update && cd .. ```
2019-02-14 14:18:15 +00:00
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
/**
* See https://stackoverflow.com/questions/25713069/why-is-wkwebview-not-opening-links-with-target-blank/25853806#25853806 for details.
*/
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
- (void)didMoveToWindow
{
if (self.window != nil && _webView == nil) {
WKWebViewConfiguration *wkWebViewConfig = [WKWebViewConfiguration new];
if (_incognito) {
wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
} else if (_cacheEnabled) {
wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
}
if(self.useSharedProcessPool) {
wkWebViewConfig.processPool = [[RNCWKProcessPoolManager sharedManager] sharedProcessPool];
}
wkWebViewConfig.userContentController = [WKUserContentController new];
if (_messagingEnabled) {
[wkWebViewConfig.userContentController addScriptMessageHandler:self name:MessageHandlerName];
NSString *source = [NSString stringWithFormat:
@"window.%@ = {"
" postMessage: function (data) {"
" window.webkit.messageHandlers.%@.postMessage(String(data));"
" }"
"};", MessageHandlerName, MessageHandlerName
];
WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
[wkWebViewConfig.userContentController addUserScript:script];
}
wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
#if WEBKIT_IOS_10_APIS_AVAILABLE
wkWebViewConfig.mediaTypesRequiringUserActionForPlayback = _mediaPlaybackRequiresUserAction
? WKAudiovisualMediaTypeAll
: WKAudiovisualMediaTypeNone;
wkWebViewConfig.dataDetectorTypes = _dataDetectorTypes;
#else
wkWebViewConfig.mediaPlaybackRequiresUserAction = _mediaPlaybackRequiresUserAction;
#endif
_webView = [[WKWebView alloc] initWithFrame:self.bounds configuration: wkWebViewConfig];
_webView.scrollView.delegate = self;
_webView.UIDelegate = self;
_webView.navigationDelegate = self;
_webView.scrollView.scrollEnabled = _scrollEnabled;
_webView.scrollView.pagingEnabled = _pagingEnabled;
_webView.scrollView.bounces = _bounces;
_webView.scrollView.showsHorizontalScrollIndicator = _showsHorizontalScrollIndicator;
_webView.scrollView.showsVerticalScrollIndicator = _showsVerticalScrollIndicator;
_webView.scrollView.directionalLockEnabled = _directionalLockEnabled;
_webView.allowsLinkPreview = _allowsLinkPreview;
[_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
_webView.allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures;
if (_userAgent) {
_webView.customUserAgent = _userAgent;
}
#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];
[self setHideKeyboardAccessoryView: _savedHideKeyboardAccessoryView];
[self visitSource];
}
}
// Update webview property when the component prop changes.
- (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures {
_allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
_webView.allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures;
}
- (void)removeFromSuperview
{
if (_webView) {
[_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHandlerName];
[_webView removeObserver:self forKeyPath:@"estimatedProgress"];
[_webView removeFromSuperview];
fix(WKWebView): resolved crash with WebKit on iOS 9.x. (#342) This PR fixes two crashes when enableWebKit is true (which was the default) on iOS 9.x. The first one when the WebView component was mounted (fixes issue #150) and the second one, when the component was unmounted (fixes issue #213). The first problem happen when the RNCWKWebView.m was loaded: The programming guide for WebKit (version 2006) ([pdf](http://mirror.informatimago.com/next/developer.apple.com/documentation/Cocoa/Conceptual/DisplayWebContent/WebKit_DisplayWebContent.pdf)) from Apple said, that it is (was) required to check if the 'new' WebKit Framework was available. This was required when the WebKit framework was only available on Mac OS X (10.2) when the Safari was installed. 😆 Because WebKit is (currently..) a fix part of iOS we didn't need this check this anymore to determinate if WebKit is available. I also see no other reference that this is required in iOS so I just remove this code. Without this code the WebView works also on iOS 9.x. The second issue happen when the WKWebView was removed from the native view hierarchy. Its required (only on iOS 9) to remove the UIScrollView delegate before cleaning up the webview. Its possible to try this PR with: ``` npm install --save "react-native-webview@jerolimov/react-native-webview#fix-ios9-crash" # or yarn add "react-native-webview@jerolimov/react-native-webview#fix-ios9-crash" ``` If you use CocoaPods, you should also update your Pods with ``` cd ios && pod update && cd .. ```
2019-02-14 14:18:15 +00:00
_webView.scrollView.delegate = nil;
_webView = nil;
}
[super removeFromSuperview];
}
-(void)keyboardWillHide
{
keyboardTimer = [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(keyboardDisplacementFix) userInfo:nil repeats:false];
[[NSRunLoop mainRunLoop] addTimer:keyboardTimer forMode:NSRunLoopCommonModes];
}
-(void)keyboardWillShow
{
if (keyboardTimer != nil) {
[keyboardTimer invalidate];
}
}
-(void)keyboardDisplacementFix
{
// Additional viewport checks to prevent unintentional scrolls
UIScrollView *scrollView = self.webView.scrollView;
double maxContentOffset = scrollView.contentSize.height - scrollView.frame.size.height;
if (maxContentOffset < 0) {
maxContentOffset = 0;
}
if (scrollView.contentOffset.y > maxContentOffset) {
// https://stackoverflow.com/a/9637807/824966
[UIView animateWithDuration:.25 animations:^{
scrollView.contentOffset = CGPointMake(0, maxContentOffset);
}];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
if(_onLoadingProgress){
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary:@{@"progress":[NSNumber numberWithDouble:self.webView.estimatedProgress]}];
_onLoadingProgress(event);
}
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
_savedBackgroundColor = backgroundColor;
if (_webView == nil) {
return;
}
CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
self.opaque = _webView.opaque = (alpha == 1.0);
_webView.scrollView.backgroundColor = backgroundColor;
_webView.backgroundColor = backgroundColor;
}
/**
* This method is called whenever JavaScript running within the web view calls:
* - window.webkit.messageHandlers[MessageHandlerName].postMessage
*/
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message
{
if (_onMessage != nil) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{@"data": message.body}];
_onMessage(event);
}
}
- (void)setSource:(NSDictionary *)source
{
if (![_source isEqualToDictionary:source]) {
_source = [source copy];
if (_webView != nil) {
[self visitSource];
}
}
}
- (void)setContentInset:(UIEdgeInsets)contentInset
{
_contentInset = contentInset;
[RCTView autoAdjustInsetsForView:self
withScrollView:_webView.scrollView
updateOffset:NO];
}
- (void)refreshContentInset
{
[RCTView autoAdjustInsetsForView:self
withScrollView:_webView.scrollView
updateOffset:YES];
}
- (void)visitSource
{
// 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.URL]) {
return;
}
if (!request.URL) {
// Clear the webview
[_webView loadHTMLString:@"" baseURL:nil];
return;
}
[_webView loadRequest:request];
}
-(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView
{
if (_webView == nil) {
_savedHideKeyboardAccessoryView = hideKeyboardAccessoryView;
return;
}
if (_savedHideKeyboardAccessoryView == false) {
return;
}
UIView* subview;
for (UIView* view in _webView.scrollView.subviews) {
if([[view.class description] hasPrefix:@"WK"])
subview = view;
}
if(subview == nil) return;
NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelperWK", subview.class.superclass];
Class newClass = NSClassFromString(name);
if(newClass == nil)
{
newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0);
if(!newClass) return;
Method method = class_getInstanceMethod([_SwizzleHelperWK class], @selector(inputAccessoryView));
class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method));
objc_registerClassPair(newClass);
}
object_setClass(subview, newClass);
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
scrollView.decelerationRate = _decelerationRate;
}
- (void)setScrollEnabled:(BOOL)scrollEnabled
{
_scrollEnabled = scrollEnabled;
_webView.scrollView.scrollEnabled = scrollEnabled;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// Don't allow scrolling the scrollView.
if (!_scrollEnabled) {
scrollView.bounds = _webView.bounds;
}
}
- (void)setDirectionalLockEnabled:(BOOL)directionalLockEnabled
{
_directionalLockEnabled = directionalLockEnabled;
_webView.scrollView.directionalLockEnabled = directionalLockEnabled;
}
- (void)setShowsHorizontalScrollIndicator:(BOOL)showsHorizontalScrollIndicator
{
_showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
_webView.scrollView.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator;
}
- (void)setShowsVerticalScrollIndicator:(BOOL)showsVerticalScrollIndicator
{
_showsVerticalScrollIndicator = showsVerticalScrollIndicator;
_webView.scrollView.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
}
- (void)postMessage:(NSString *)message
{
NSDictionary *eventInitDict = @{@"data": message};
NSString *source = [NSString
stringWithFormat:@"window.dispatchEvent(new MessageEvent('message', %@));",
RCTJSONStringify(eventInitDict, NULL)
];
[self injectJavaScript: source];
}
- (void)layoutSubviews
{
[super layoutSubviews];
2018-09-18 16:32:16 +00:00
// Ensure webview takes the position and dimensions of RNCWKWebView
_webView.frame = self.bounds;
}
- (NSMutableDictionary<NSString *, id> *)baseEvent
{
NSDictionary *event = @{
@"url": _webView.URL.absoluteString ?: @"",
@"title": _webView.title,
@"loading" : @(_webView.loading),
@"canGoBack": @(_webView.canGoBack),
@"canGoForward" : @(_webView.canGoForward)
};
return [[NSMutableDictionary alloc] initWithDictionary: event];
}
+ (void)setClientAuthenticationCredential:(nullable NSURLCredential*)credential {
clientAuthenticationCredential = credential;
}
- (void) webView:(WKWebView *)webView
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable))completionHandler
{
if (!clientAuthenticationCredential) {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
return;
}
if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodClientCertificate) {
completionHandler(NSURLSessionAuthChallengeUseCredential, clientAuthenticationCredential);
} else {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
}
#pragma mark - WKNavigationDelegate methods
/**
* alert
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
completionHandler();
}]];
[[self topViewController] presentViewController:alert animated:YES completion:NULL];
}
/**
* confirm
*/
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
completionHandler(YES);
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
completionHandler(NO);
}]];
[[self topViewController] presentViewController:alert animated:YES completion:NULL];
}
/**
* prompt
*/
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.textColor = [UIColor lightGrayColor];
textField.placeholder = defaultText;
}];
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
completionHandler([[alert.textFields lastObject] text]);
}]];
[[self topViewController] presentViewController:alert animated:YES completion:NULL];
}
/**
* topViewController
*/
-(UIViewController *)topViewController{
   UIViewController *controller = [self topViewControllerWithRootViewController:[self getCurrentWindow].rootViewController];
   return controller;
}
/**
* topViewControllerWithRootViewController
*/
-(UIViewController *)topViewControllerWithRootViewController:(UIViewController *)viewController{
if (viewController==nil) return nil;
if (viewController.presentedViewController!=nil) {
return [self topViewControllerWithRootViewController:viewController.presentedViewController];
} else if ([viewController isKindOfClass:[UITabBarController class]]){
return [self topViewControllerWithRootViewController:[(UITabBarController *)viewController selectedViewController]];
} else if ([viewController isKindOfClass:[UINavigationController class]]){
return [self topViewControllerWithRootViewController:[(UINavigationController *)viewController visibleViewController]];
} else {
return viewController;
}
}
/**
* getCurrentWindow
*/
-(UIWindow *)getCurrentWindow{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
if (window.windowLevel!=UIWindowLevelNormal) {
for (UIWindow *wid in [UIApplication sharedApplication].windows) {
if (window.windowLevel==UIWindowLevelNormal) {
window = wid;
break;
}
}
}
return window;
}
/**
* Decides whether to allow or cancel a navigation.
* @see https://fburl.com/42r9fxob
*/
- (void) webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
static NSDictionary<NSNumber *, NSString *> *navigationTypes;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
navigationTypes = @{
@(WKNavigationTypeLinkActivated): @"click",
@(WKNavigationTypeFormSubmitted): @"formsubmit",
@(WKNavigationTypeBackForward): @"backforward",
@(WKNavigationTypeReload): @"reload",
@(WKNavigationTypeFormResubmitted): @"formresubmit",
@(WKNavigationTypeOther): @"other",
};
});
WKNavigationType navigationType = navigationAction.navigationType;
NSURLRequest *request = navigationAction.request;
if (_onShouldStartLoadWithRequest) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"url": (request.URL).absoluteString,
@"navigationType": navigationTypes[@(navigationType)]
}];
if (![self.delegate webView:self
shouldStartLoadForRequest:event
withCallback:_onShouldStartLoadWithRequest]) {
decisionHandler(WKNavigationResponsePolicyCancel);
return;
}
}
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);
}
}
// Allow all navigation by default
decisionHandler(WKNavigationResponsePolicyAllow);
}
/**
* Called when an error occurs while the web view is loading content.
* @see https://fburl.com/km6vqenw
*/
- (void) webView:(WKWebView *)webView
didFailProvisionalNavigation:(WKNavigation *)navigation
withError:(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 WKWebView
// 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:@{
@"didFailProvisionalNavigation": @YES,
@"domain": error.domain,
@"code": @(error.code),
@"description": error.localizedDescription,
}];
_onLoadingError(event);
}
[self setBackgroundColor: _savedBackgroundColor];
}
- (void)evaluateJS:(NSString *)js
thenCall: (void (^)(NSString*)) callback
{
[self.webView evaluateJavaScript: js completionHandler: ^(id result, NSError *error) {
if (error == nil) {
if (callback != nil) {
callback([NSString stringWithFormat:@"%@", result]);
}
} else {
RCTLogError(@"Error evaluating injectedJavaScript: This is possibly due to an unsupported return type. Try adding true to the end of your injectedJavaScript string.");
}
}];
}
/**
* Called when the navigation is complete.
* @see https://fburl.com/rtys6jlb
*/
- (void) webView:(WKWebView *)webView
didFinishNavigation:(WKNavigation *)navigation
{
if (_injectedJavaScript) {
[self evaluateJS: _injectedJavaScript thenCall: ^(NSString *jsEvaluationValue) {
NSMutableDictionary *event = [self baseEvent];
event[@"jsEvaluationValue"] = jsEvaluationValue;
if (self.onLoadingFinish) {
self.onLoadingFinish(event);
}
}];
} else if (_onLoadingFinish) {
_onLoadingFinish([self baseEvent]);
}
[self setBackgroundColor: _savedBackgroundColor];
}
- (void)injectJavaScript:(NSString *)script
{
[self evaluateJS: script thenCall: nil];
}
- (void)goForward
{
[_webView goForward];
}
- (void)goBack
{
[_webView goBack];
}
- (void)reload
{
/**
* When the initial load fails due to network connectivity issues,
* [_webView reload] doesn't reload the webpage. Therefore, we must
* manually call [_webView loadRequest:request].
*/
NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
if (request.URL && !_webView.URL.absoluteString.length) {
[_webView loadRequest:request];
}
else {
[_webView reload];
}
}
- (void)stopLoading
{
[_webView stopLoading];
}
- (void)setBounces:(BOOL)bounces
{
_bounces = bounces;
_webView.scrollView.bounces = bounces;
}
@end