5

I have a website that uses javascript that uses angular to control what you see on the website. So http://somewebsite.com/#page1 is shown and when you click on a tab that location changes to http://somewebsite.com/#page2. There will be no actual reload on the website and therefor func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) will not get triggered. How can you catch these hash changes?

Clearly Apple can, since the Safari webbrowser is able to show these changes in the location bar.


Update 19.33:

I did some research. Using window.onhashchange in the Safari browser this will get fired however in the WKWebView it does not. Diving deeper I use the following javascript to return the document location href every second.

window.setInterval(function(){ alert(document.location) }, 1000);

When reading that alert in WKWebView it never shows that the location changes, while in Safari you see http://somewebsite.com/#page1 and http://somewebsite.com/#page2. It seems like WKWebView does not support this feature which is extremely annoying.

Mark
  • 16,906
  • 20
  • 84
  • 117

2 Answers2

4

No need to inject any JS. I used webView.addObserver(self, forKeyPath: "URL", options: .new, context: nil) and this was detecting any URL changes, including hash changes. Then:

override func observeValue(
    forKeyPath keyPath: String?,
    of object: Any?,
    change: [NSKeyValueChangeKey: Any]?,
    context: UnsafeMutableRawPointer?) {
  if object as AnyObject? === webView && keyPath == "URL" {
    onURLChange(webView.url) // onURLChange does whatever you want
  }
}

Don't forget to webView.removeObserver(self, forKeyPath: "URL") in deinit block though.

NeverwinterMoon
  • 2,363
  • 21
  • 24
1
let script = WKUserScript(source: "(function() { \"use strict\"; addEventListener(\"hashchange\", function(event) { webkit.messageHandlers.hashchangeMessageHandler.postMessage(null); }); }) ();", injectionTime: WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: false)

let configuration = WKWebViewConfiguration()
configuration.userContentController.add(self, name: "hashchangeMessageHandler")
configuration.userContentController.addUserScript(script)

self.webView = WKWebView(frame: CGRect.zero, configuration: configuration)

Now you can fetch the hash changed URL with:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    print("Received message: \(message.name): \(message.body)")
    print(self.webView.url)

}
Mark
  • 16,906
  • 20
  • 84
  • 117