Skip to content

Commit 3615296

Browse files
safaiyehTitozzz
authored andcommitted
feat(ios): Generate history API events on iOS (#1082)
BREAKING CHANGE: if you use onNavigationStateChange on iOS it will now trigger on # changes to the url. * Hook the `window.history` API on iOS to generate events The underlying WKWebView doesn't seem to generate any events in response to the `window.history` API - none of the `WKNavigationDelegate` methods fire. Given this limitation, the only way to know when the location changes via this API is to inject Javascript into the page and have it notify the native code directly when any of these functions are called. The `setTimeout` call gives up the current tick, allowing the location to change before firing the event. * Remove the outdated section about hash changes Now that this bug is fixed, the workaround is no longer required.
1 parent c400133 commit 3615296

File tree

2 files changed

+38
-42
lines changed

2 files changed

+38
-42
lines changed

docs/Guide.md

-38
Original file line numberDiff line numberDiff line change
@@ -149,44 +149,6 @@ class MyWeb extends Component {
149149
}
150150
```
151151

152-
#### Intercepting hash URL changes
153-
154-
While `onNavigationStateChange` will trigger on URL changes, it does not trigger when only the hash URL ("anchor") changes, e.g. from `https://example.com/users#list` to `https://example.com/users#help`.
155-
156-
You can inject some JavaScript to wrap the history functions in order to intercept these hash URL changes.
157-
158-
```jsx
159-
<WebView
160-
source={{ uri: someURI }}
161-
injectedJavaScript={`
162-
(function() {
163-
function wrap(fn) {
164-
return function wrapper() {
165-
var res = fn.apply(this, arguments);
166-
window.ReactNativeWebView.postMessage('navigationStateChange');
167-
return res;
168-
}
169-
}
170-
171-
history.pushState = wrap(history.pushState);
172-
history.replaceState = wrap(history.replaceState);
173-
window.addEventListener('popstate', function() {
174-
window.ReactNativeWebView.postMessage('navigationStateChange');
175-
});
176-
})();
177-
178-
true;
179-
`}
180-
onMessage={({ nativeEvent: state }) => {
181-
if (state.data === 'navigationStateChange') {
182-
// Navigation state updated, can check state.canGoBack, etc.
183-
}
184-
}}
185-
/>
186-
```
187-
188-
Thanks to [Janic Duplessis](https://github.com/react-native-community/react-native-webview/issues/24#issuecomment-483956651) for this workaround.
189-
190152
### Add support for File Upload
191153

192154
##### iOS

ios/RNCWebView.m

+38-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#import "objc/runtime.h"
1515

1616
static NSTimer *keyboardTimer;
17+
static NSString *const HistoryShimName = @"ReactNativeHistoryShim";
1718
static NSString *const MessageHandlerName = @"ReactNativeWebView";
1819
static NSURLCredential* clientAuthenticationCredential;
1920
static NSDictionary* customCertificatesForHost;
@@ -160,6 +161,31 @@ - (void)didMoveToWindow
160161
}
161162
wkWebViewConfig.userContentController = [WKUserContentController new];
162163

164+
// Shim the HTML5 history API:
165+
[wkWebViewConfig.userContentController addScriptMessageHandler:self name:HistoryShimName];
166+
NSString *source = [NSString stringWithFormat:
167+
@"(function(history) {\n"
168+
" function notify(type) {\n"
169+
" setTimeout(function() {\n"
170+
" window.webkit.messageHandlers.%@.postMessage(type)\n"
171+
" }, 0)\n"
172+
" }\n"
173+
" function shim(f) {\n"
174+
" return function pushState() {\n"
175+
" notify('other')\n"
176+
" return f.apply(history, arguments)\n"
177+
" }\n"
178+
" }\n"
179+
" history.pushState = shim(history.pushState)\n"
180+
" history.replaceState = shim(history.replaceState)\n"
181+
" window.addEventListener('popstate', function() {\n"
182+
" notify('backforward')\n"
183+
" })\n"
184+
"})(window.history)\n", HistoryShimName
185+
];
186+
WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
187+
[wkWebViewConfig.userContentController addUserScript:script];
188+
163189
if (_messagingEnabled) {
164190
[wkWebViewConfig.userContentController addScriptMessageHandler:self name:MessageHandlerName];
165191

@@ -404,10 +430,18 @@ - (void)setContentInsetAdjustmentBehavior:(UIScrollViewContentInsetAdjustmentBeh
404430
- (void)userContentController:(WKUserContentController *)userContentController
405431
didReceiveScriptMessage:(WKScriptMessage *)message
406432
{
407-
if (_onMessage != nil) {
408-
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
409-
[event addEntriesFromDictionary: @{@"data": message.body}];
410-
_onMessage(event);
433+
if ([message.name isEqualToString:HistoryShimName]) {
434+
if (_onLoadingFinish) {
435+
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
436+
[event addEntriesFromDictionary: @{@"navigationType": message.body}];
437+
_onLoadingFinish(event);
438+
}
439+
} else if ([message.name isEqualToString:MessageHandlerName]) {
440+
if (_onMessage) {
441+
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
442+
[event addEntriesFromDictionary: @{@"data": message.body}];
443+
_onMessage(event);
444+
}
411445
}
412446
}
413447

0 commit comments

Comments
 (0)