Notes on listening to location changes in WKWebView

Hi, I'm Hwee-Boon Yar

I write, ship and sell software products. iOS/OS X app developer based in Singapore, working remotely. This is my blog.


Need to run a code review on your codebase? Hire me

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:

1. 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(){

2. Inject the JavaScript above is to install a user script:

let webViewConfig = WKWebViewConfiguration()
let userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: false)
//Install handler (below)
let webView = WKWebView(frame: .zero, configuration: config)

3. Specify the message handler:

webViewConfig.userContentController.add(messageHandler, name: "locationChanged")

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

Like this post? Follow me @hboon on Twitter.