Skip to content

Commit

Permalink
feat(ios): Generate history API events on iOS (react-native-webview#1082
Browse files Browse the repository at this point in the history
)

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.
  • Loading branch information
safaiyeh authored and Titozzz committed Dec 16, 2019
1 parent 80eea30 commit 4081c85
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 42 deletions.
38 changes: 0 additions & 38 deletions docs/Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,44 +149,6 @@ class MyWeb extends Component {
}
```

#### Intercepting hash URL changes

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`.

You can inject some JavaScript to wrap the history functions in order to intercept these hash URL changes.

```jsx
<WebView
source={{ uri: someURI }}
injectedJavaScript={`
(function() {
function wrap(fn) {
return function wrapper() {
var res = fn.apply(this, arguments);
window.ReactNativeWebView.postMessage('navigationStateChange');
return res;
}
}
history.pushState = wrap(history.pushState);
history.replaceState = wrap(history.replaceState);
window.addEventListener('popstate', function() {
window.ReactNativeWebView.postMessage('navigationStateChange');
});
})();
true;
`}
onMessage={({ nativeEvent: state }) => {
if (state.data === 'navigationStateChange') {
// Navigation state updated, can check state.canGoBack, etc.
}
}}
/>
```

Thanks to [Janic Duplessis](https://github.com/react-native-community/react-native-webview/issues/24#issuecomment-483956651) for this workaround.

### Add support for File Upload

##### iOS
Expand Down
42 changes: 38 additions & 4 deletions ios/RNCWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#import "objc/runtime.h"

static NSTimer *keyboardTimer;
static NSString *const HistoryShimName = @"ReactNativeHistoryShim";
static NSString *const MessageHandlerName = @"ReactNativeWebView";
static NSURLCredential* clientAuthenticationCredential;
static NSDictionary* customCertificatesForHost;
Expand Down Expand Up @@ -160,6 +161,31 @@ - (void)didMoveToWindow
}
wkWebViewConfig.userContentController = [WKUserContentController new];

// Shim the HTML5 history API:
[wkWebViewConfig.userContentController addScriptMessageHandler:self name:HistoryShimName];
NSString *source = [NSString stringWithFormat:
@"(function(history) {\n"
" function notify(type) {\n"
" setTimeout(function() {\n"
" window.webkit.messageHandlers.%@.postMessage(type)\n"
" }, 0)\n"
" }\n"
" function shim(f) {\n"
" return function pushState() {\n"
" notify('other')\n"
" return f.apply(history, arguments)\n"
" }\n"
" }\n"
" history.pushState = shim(history.pushState)\n"
" history.replaceState = shim(history.replaceState)\n"
" window.addEventListener('popstate', function() {\n"
" notify('backforward')\n"
" })\n"
"})(window.history)\n", HistoryShimName
];
WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
[wkWebViewConfig.userContentController addUserScript:script];

if (_messagingEnabled) {
[wkWebViewConfig.userContentController addScriptMessageHandler:self name:MessageHandlerName];

Expand Down Expand Up @@ -404,10 +430,18 @@ - (void)setContentInsetAdjustmentBehavior:(UIScrollViewContentInsetAdjustmentBeh
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message
{
if (_onMessage != nil) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{@"data": message.body}];
_onMessage(event);
if ([message.name isEqualToString:HistoryShimName]) {
if (_onLoadingFinish) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{@"navigationType": message.body}];
_onLoadingFinish(event);
}
} else if ([message.name isEqualToString:MessageHandlerName]) {
if (_onMessage) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{@"data": message.body}];
_onMessage(event);
}
}
}

Expand Down

0 comments on commit 4081c85

Please sign in to comment.