How to read console logs of wkwebview programmatically How to read console logs of wkwebview programmatically javascript javascript

How to read console logs of wkwebview programmatically


It's possible to connect Safari browser on you Mac to the WKWebView and get access to the console.

From Safari, open "Develop" tab and while the iOS Simulator is running with the WKWebView open - just click it to open the console. See:

enter image description here


This worked for me (Swift 4.2/5):

// inject JS to capture console.log output and send to iOSlet source = "function captureLog(msg) { window.webkit.messageHandlers.logHandler.postMessage(msg); } window.console.log = captureLog;"let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)webView.configuration.userContentController.addUserScript(script)// register the bridge script that listens for the outputwebView.configuration.userContentController.add(self, name: "logHandler")

Then, conforming to the protocol WKScriptMessageHandler, pick up redirected console messages with the following:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {    if message.name == "logHandler" {        print("LOG: \(message.body)")      }}


I needed a way to see JavaScript logs in Xcode's console. Based on the answer by noxo, here's what I came up with:

let overrideConsole = """    function log(emoji, type, args) {      window.webkit.messageHandlers.logging.postMessage(        `${emoji} JS ${type}: ${Object.values(args)          .map(v => typeof(v) === "undefined" ? "undefined" : typeof(v) === "object" ? JSON.stringify(v) : v.toString())          .map(v => v.substring(0, 3000)) // Limit msg to 3000 chars          .join(", ")}`      )    }    let originalLog = console.log    let originalWarn = console.warn    let originalError = console.error    let originalDebug = console.debug    console.log = function() { log("📗", "log", arguments); originalLog.apply(null, arguments) }    console.warn = function() { log("📙", "warning", arguments); originalWarn.apply(null, arguments) }    console.error = function() { log("📕", "error", arguments); originalError.apply(null, arguments) }    console.debug = function() { log("📘", "debug", arguments); originalDebug.apply(null, arguments) }    window.addEventListener("error", function(e) {       log("💥", "Uncaught", [`${e.message} at ${e.filename}:${e.lineno}:${e.colno}`])    })"""class LoggingMessageHandler: NSObject, WKScriptMessageHandler {    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {        print(message.body)    }}let userContentController = WKUserContentController()userContentController.add(LoggingMessageHandler(), name: "logging")userContentController.addUserScript(WKUserScript(source: overrideConsole, injectionTime: .atDocumentStart, forMainFrameOnly: true))let webViewConfig = WKWebViewConfiguration()webViewConfig.userContentController = userContentControllerlet webView = WKWebView(frame: .zero, configuration: webViewConfig)

It has a few improvements:

  • It still calls the original log function, in case you decide to look in the Web Inspector
  • It reports from both log, warn, error and debug
  • It adds a nice emoji so you can easily distinguish the different kinds og logs and JS logs stands out in the Xcode console
  • It logs all arguments given to console.log, not just the first one
  • It logs uncaught errors, in case you need that