Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read console logs of wkwebview programmatically

I am trying to read the console logs of webapp that is loaded in my WkWebview programmatically.

so far in my research it's not possible.

How can I achieve this?

like image 604
NaveenKumar Avatar asked May 11 '16 10:05

NaveenKumar


3 Answers

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

like image 148
Oded Regev Avatar answered Oct 20 '22 09:10

Oded Regev


This worked for me (Swift 4.2/5):

// inject JS to capture console.log output and send to iOS
let 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 output
webView.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)")  
    }
}
like image 28
Richard West-Soley Avatar answered Oct 20 '22 09:10

Richard West-Soley


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 = userContentController

let 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
like image 26
Soeholm Avatar answered Oct 20 '22 09:10

Soeholm