Implement onLoading(Start|Finish|Error) and injectedJavaScript props
Summary: @public This diff includes four very straightforward changes: 1. Whenever the WebView starts loading, the `onLoadingStart` hook should be executed. The event passed into this hook should be decorated with the `navigationType`. 1. Whenever the WebView errors out while loading, the `onLoadingError` hook should be executed. 1. Whenever the WebView finishes loading (without any errors), the `onLoadingFinish` hook should be executed. 1. The serialized JavaScript passed into the `injectedJavaScript` prop should be executed when the WebView finishes loading. After execution finishes, the `onLoadingFinish` prop should be called with the serialized completion value of this JavaScript. Reviewed By: shergin Differential Revision: D6293532 fbshipit-source-id: 21407c766f73413046823ae605afc21e85cf9db6
This commit is contained in:
parent
1442c265da
commit
3703927e7e
|
@ -18,5 +18,6 @@
|
|||
|
||||
@property (nonatomic, weak) id<RCTWKWebViewDelegate> delegate;
|
||||
@property (nonatomic, copy) NSDictionary *source;
|
||||
@property (nonatomic, copy) NSString *injectedJavaScript;
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
|
||||
#import "RCTAutoInsetsProtocol.h"
|
||||
|
||||
@interface RCTWKWebView () <WKUIDelegate>
|
||||
@interface RCTWKWebView () <WKUIDelegate, WKNavigationDelegate>
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
|
||||
@property (nonatomic, copy) WKWebView *webView;
|
||||
@end
|
||||
|
||||
@implementation RCTWKWebView
|
||||
{
|
||||
WKWebView *_webView;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
|
@ -25,6 +28,7 @@
|
|||
super.backgroundColor = [UIColor clearColor];
|
||||
_webView = [[WKWebView alloc] initWithFrame:self.bounds];
|
||||
_webView.UIDelegate = self;
|
||||
_webView.navigationDelegate = self;
|
||||
[self addSubview:_webView];
|
||||
}
|
||||
return self;
|
||||
|
@ -69,4 +73,119 @@
|
|||
_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];
|
||||
}
|
||||
|
||||
#pragma mark - WKNavigationDelegate methods
|
||||
|
||||
/**
|
||||
* 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 (_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;
|
||||
}
|
||||
|
||||
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
|
||||
[event addEntriesFromDictionary:@{
|
||||
@"didFailProvisionalNavigation": @YES,
|
||||
@"domain": error.domain,
|
||||
@"code": @(error.code),
|
||||
@"description": error.localizedDescription,
|
||||
}];
|
||||
_onLoadingError(event);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)evaluateJS:(NSString *)js
|
||||
thenCall: (void (^)(NSString*)) callback
|
||||
{
|
||||
[self.webView evaluateJavaScript: js completionHandler: ^(id result, NSError *error) {
|
||||
if (error == nil) {
|
||||
callback([NSString stringWithFormat:@"%@", result]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -14,5 +14,9 @@ RCT_EXPORT_MODULE()
|
|||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue