Skip to content

Commit f7f9d01

Browse files
RSNarakelset
authored andcommitted
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
1 parent 9b3a6ec commit f7f9d01

File tree

3 files changed

+126
-2
lines changed

3 files changed

+126
-2
lines changed

React/Views/RCTWKWebView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@
1818

1919
@property (nonatomic, weak) id<RCTWKWebViewDelegate> delegate;
2020
@property (nonatomic, copy) NSDictionary *source;
21+
@property (nonatomic, copy) NSString *injectedJavaScript;
2122

2223
@end

React/Views/RCTWKWebView.m

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66

77
#import "RCTAutoInsetsProtocol.h"
88

9-
@interface RCTWKWebView () <WKUIDelegate>
9+
@interface RCTWKWebView () <WKUIDelegate, WKNavigationDelegate>
10+
@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
11+
@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
12+
@property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
13+
@property (nonatomic, copy) WKWebView *webView;
1014
@end
1115

1216
@implementation RCTWKWebView
1317
{
14-
WKWebView *_webView;
1518
}
1619

1720
- (void)dealloc
@@ -25,6 +28,7 @@ - (instancetype)initWithFrame:(CGRect)frame
2528
super.backgroundColor = [UIColor clearColor];
2629
_webView = [[WKWebView alloc] initWithFrame:self.bounds];
2730
_webView.UIDelegate = self;
31+
_webView.navigationDelegate = self;
2832
[self addSubview:_webView];
2933
}
3034
return self;
@@ -69,4 +73,119 @@ - (void)layoutSubviews
6973
_webView.frame = self.bounds;
7074
}
7175

76+
- (NSMutableDictionary<NSString *, id> *)baseEvent
77+
{
78+
NSDictionary *event = @{
79+
@"url": _webView.URL.absoluteString ?: @"",
80+
@"title": _webView.title,
81+
@"loading" : @(_webView.loading),
82+
@"canGoBack": @(_webView.canGoBack),
83+
@"canGoForward" : @(_webView.canGoForward)
84+
};
85+
return [[NSMutableDictionary alloc] initWithDictionary: event];
86+
}
87+
88+
#pragma mark - WKNavigationDelegate methods
89+
90+
/**
91+
* Decides whether to allow or cancel a navigation.
92+
* @see https://fburl.com/42r9fxob
93+
*/
94+
- (void) webView:(WKWebView *)webView
95+
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
96+
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
97+
{
98+
static NSDictionary<NSNumber *, NSString *> *navigationTypes;
99+
static dispatch_once_t onceToken;
100+
101+
dispatch_once(&onceToken, ^{
102+
navigationTypes = @{
103+
@(WKNavigationTypeLinkActivated): @"click",
104+
@(WKNavigationTypeFormSubmitted): @"formsubmit",
105+
@(WKNavigationTypeBackForward): @"backforward",
106+
@(WKNavigationTypeReload): @"reload",
107+
@(WKNavigationTypeFormResubmitted): @"formresubmit",
108+
@(WKNavigationTypeOther): @"other",
109+
};
110+
});
111+
112+
WKNavigationType navigationType = navigationAction.navigationType;
113+
NSURLRequest *request = navigationAction.request;
114+
115+
if (_onLoadingStart) {
116+
// We have this check to filter out iframe requests and whatnot
117+
BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
118+
if (isTopFrame) {
119+
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
120+
[event addEntriesFromDictionary: @{
121+
@"url": (request.URL).absoluteString,
122+
@"navigationType": navigationTypes[@(navigationType)]
123+
}];
124+
_onLoadingStart(event);
125+
}
126+
}
127+
128+
// Allow all navigation by default
129+
decisionHandler(WKNavigationResponsePolicyAllow);
130+
}
131+
132+
/**
133+
* Called when an error occurs while the web view is loading content.
134+
* @see https://fburl.com/km6vqenw
135+
*/
136+
- (void) webView:(WKWebView *)webView
137+
didFailProvisionalNavigation:(WKNavigation *)navigation
138+
withError:(NSError *)error
139+
{
140+
if (_onLoadingError) {
141+
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) {
142+
// NSURLErrorCancelled is reported when a page has a redirect OR if you load
143+
// a new URL in the WebView before the previous one came back. We can just
144+
// ignore these since they aren't real errors.
145+
// http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os
146+
return;
147+
}
148+
149+
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
150+
[event addEntriesFromDictionary:@{
151+
@"didFailProvisionalNavigation": @YES,
152+
@"domain": error.domain,
153+
@"code": @(error.code),
154+
@"description": error.localizedDescription,
155+
}];
156+
_onLoadingError(event);
157+
}
158+
}
159+
160+
- (void)evaluateJS:(NSString *)js
161+
thenCall: (void (^)(NSString*)) callback
162+
{
163+
[self.webView evaluateJavaScript: js completionHandler: ^(id result, NSError *error) {
164+
if (error == nil) {
165+
callback([NSString stringWithFormat:@"%@", result]);
166+
}
167+
}];
168+
}
169+
170+
171+
/**
172+
* Called when the navigation is complete.
173+
* @see https://fburl.com/rtys6jlb
174+
*/
175+
- (void) webView:(WKWebView *)webView
176+
didFinishNavigation:(WKNavigation *)navigation
177+
{
178+
if (_injectedJavaScript) {
179+
[self evaluateJS: _injectedJavaScript thenCall: ^(NSString *jsEvaluationValue) {
180+
NSMutableDictionary *event = [self baseEvent];
181+
event[@"jsEvaluationValue"] = jsEvaluationValue;
182+
if (self.onLoadingFinish) {
183+
self.onLoadingFinish(event);
184+
}
185+
}];
186+
} else if (_onLoadingFinish) {
187+
_onLoadingFinish([self baseEvent]);
188+
}
189+
}
190+
72191
@end

React/Views/RCTWKWebViewManager.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@ - (UIView *)view
1414
}
1515

1616
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
17+
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
18+
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
19+
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
20+
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
1721

1822
@end

0 commit comments

Comments
 (0)