Notes on listening to location changes in WKWebView
WKNavigationDelegate is useful for tracking when user navigates to a different URL with functions such as webView(_:,decidePolicyFor:,decisionHandler:). But if the site uses HTML History API to the location (common with React and friends), it doesn't pick it up. Here's how to support it:
- This JavaScript to dispatch a message to the WKWebView instance's message handler:
// So we can detect when sites use History API to generate the page location. Especially common with React and similar frameworks ;(function() { var pushState = history.pushState; var replaceState = history.replaceState;
history.pushState = function() { pushState.apply(history, arguments); window.dispatchEvent(new Event('locationchange')); };
history.replaceState = function() { replaceState.apply(history, arguments); window.dispatchEvent(new Event('locationchange')); };
window.addEventListener('popstate', function() { window.dispatchEvent(new Event('locationchange')) }); })();
window.addEventListener('locationchange', function(){ webkit.messageHandlers.locationChanged.postMessage(window.location.href) })
- Inject the JavaScript above is to install a user script:
let webViewConfig = WKWebViewConfiguration() let userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: false) webViewConfig.userContentController.addUserScript(userScript) //Install handler (below) let webView = WKWebView(frame: .zero, configuration: config)
- Specify the message handler:
webViewConfig.userContentController.add(messageHandler, name: "locationChanged")
- Handle the message, getting the URL:
extension SomeClass: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//get the URL from message or just from webView.url
}
}
Thanks to the suggestion from @hishnash.
Discussion in the ATmosphere