{
  "$type": "site.standard.document",
  "description": "WKNavigationDelegate is useful for tracking when user navigates to a different URL with functions such as webView :,decidePolicyFor:,decisionHandler:",
  "path": "/notes-wkweb-view-location-change-history-aoi/",
  "publishedAt": "2021-08-24T07:12:00.000Z",
  "site": "at://did:plc:bryys25pc2fnagnyxqgsglhd/site.standard.publication/3mn26bjkkmh23",
  "tags": [
    "iOS Development",
    "macOS Development",
    "Swift",
    "Techniques"
  ],
  "textContent": "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:\n\n1. This JavaScript to dispatch a message to the WKWebView instance's message handler:\n\n // So we can detect when sites use History API to generate the page location. Especially common with React and similar frameworks\n ;(function() {\n   var pushState = history.pushState;\n   var replaceState = history.replaceState;\n\n   history.pushState = function() {\n     pushState.apply(history, arguments);\n     window.dispatchEvent(new Event('locationchange'));\n   };\n\n   history.replaceState = function() {\n     replaceState.apply(history, arguments);\n     window.dispatchEvent(new Event('locationchange'));\n   };\n\n   window.addEventListener('popstate', function() {\n     window.dispatchEvent(new Event('locationchange'))\n   });\n })();\n\n window.addEventListener('locationchange', function(){\n   webkit.messageHandlers.locationChanged.postMessage(window.location.href)\n })\n\n2. Inject the JavaScript above is to install a user script:\n\nlet webViewConfig = WKWebViewConfiguration()\nlet userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: false)\nwebViewConfig.userContentController.addUserScript(userScript)\n//Install handler (below)\nlet webView = WKWebView(frame: .zero, configuration: config)  \n\n3. Specify the message handler:\n\nwebViewConfig.userContentController.add(messageHandler, name: \"locationChanged\")\n\n4. Handle the message, getting the URL:\n\n extension SomeClass: WKScriptMessageHandler {\n     func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {\n         //get the URL from `message` or just from `webView.url`\n     }\n }\n\nThanks to the suggestion from @hishnash.",
  "title": "Notes on listening to location changes in WKWebView"
}