Skip to content

Commit

Permalink
Implement onLoading(Start|Finish|Error) and injectedJavaScript props
Browse files Browse the repository at this point in the history
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
  • Loading branch information
RSNara authored and facebook-github-bot committed Aug 16, 2018
1 parent 1442c26 commit 3703927
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 2 deletions.
1 change: 1 addition & 0 deletions React/Views/RCTWKWebView.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@

@property (nonatomic, weak) id<RCTWKWebViewDelegate> delegate;
@property (nonatomic, copy) NSDictionary *source;
@property (nonatomic, copy) NSString *injectedJavaScript;

@end
123 changes: 121 additions & 2 deletions React/Views/RCTWKWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -25,6 +28,7 @@ - (instancetype)initWithFrame:(CGRect)frame
super.backgroundColor = [UIColor clearColor];
_webView = [[WKWebView alloc] initWithFrame:self.bounds];
_webView.UIDelegate = self;
_webView.navigationDelegate = self;
[self addSubview:_webView];
}
return self;
Expand Down Expand Up @@ -69,4 +73,119 @@ - (void)layoutSubviews
_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
4 changes: 4 additions & 0 deletions React/Views/RCTWKWebViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ - (UIView *)view
}

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

0 comments on commit 3703927

Please sign in to comment.