You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从WKWebView中捕获JavaScript错误及相关警告?

如何从WKWebView捕获JavaScript错误与警告

当然有办法搞定这个需求!先说说你当前代码的小问题:你写的evaluateJavaScriptWithReturn用了阻塞RunLoop的方式实现同步调用,这种做法不仅会阻塞主线程影响性能,而且只能捕获调用evaluateJavaScript时的执行错误,没法监听网页中其他JS代码运行时的错误、警告,甚至Promise未处理的拒绝这类情况。

下面给你两种常用的方案,覆盖各类JS错误和警告的捕获:

方案一:通过WKUserScript注入监听脚本,原生接收错误信息

这是最通用的方式,能捕获网页中所有JS代码的错误、警告,包括第三方脚本的问题。

步骤1:配置WKWebView并注入监听脚本

首先创建WKWebViewConfiguration,注入自定义的JS脚本,监听window.onerror、console.error、console.warn以及unhandledrejection(处理Promise未捕获的拒绝)事件,把这些信息通过window.webkit.messageHandlers发送给原生代码。

import WebKit

class JSErrorHandler: NSObject, WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard let errorInfo = message.body as? [String: Any] else { return }
        
        // 这里处理收到的JS错误/警告信息
        switch errorInfo["type"] as? String {
        case "error":
            print("JS错误: \(errorInfo["message"] ?? "未知错误"),文件: \(errorInfo["file"] ?? "未知文件"),行号: \(errorInfo["line"] ?? 0)")
        case "warn":
            print("JS警告: \(errorInfo["message"] ?? "未知警告"),文件: \(errorInfo["file"] ?? "未知文件"),行号: \(errorInfo["line"] ?? 0)")
        case "unhandledrejection":
            print("Promise未处理拒绝: \(errorInfo["reason"] ?? "未知原因")")
        default:
            break
        }
    }
}

// 初始化WKWebView的配置
let errorHandler = JSErrorHandler()
let userContentController = WKUserContentController()

// 注入监听JS错误的脚本
let jsScript = """
// 监听全局错误
window.onerror = function(message, source, line, column, error) {
    window.webkit.messageHandlers.jsErrorHandler.postMessage({
        type: 'error',
        message: message,
        file: source,
        line: line,
        column: column,
        stack: error ? error.stack : null
    });
    return true; // 阻止默认错误上报行为,可选
};

// 监听console.error
const originalConsoleError = console.error;
console.error = function(...args) {
    window.webkit.messageHandlers.jsErrorHandler.postMessage({
        type: 'error',
        message: args.join(' '),
        file: new Error().stack?.split('\\n')[1] || 'unknown',
        line: 0
    });
    originalConsoleError.apply(console, args); // 保留原console.error行为
};

// 监听console.warn
const originalConsoleWarn = console.warn;
console.warn = function(...args) {
    window.webkit.messageHandlers.jsErrorHandler.postMessage({
        type: 'warn',
        message: args.join(' '),
        file: new Error().stack?.split('\\n')[1] || 'unknown',
        line: 0
    });
    originalConsoleWarn.apply(console, args); // 保留原console.warn行为
};

// 监听未处理的Promise拒绝
window.addEventListener('unhandledrejection', function(event) {
    window.webkit.messageHandlers.jsErrorHandler.postMessage({
        type: 'unhandledrejection',
        reason: event.reason?.toString() || 'Unknown reason',
        promise: event.promise.toString()
    });
});
"""

let userScript = WKUserScript(source: jsScript, injectionTime: .atDocumentStart, forMainFrameOnly: false)
userContentController.addUserScript(userScript)
userContentController.add(errorHandler, name: "jsErrorHandler")

let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController

// 创建WKWebView
let webView = WKWebView(frame: .zero, configuration: configuration)

步骤2:处理原生收到的错误信息

上面的JSErrorHandler类实现了WKScriptMessageHandler协议,在didReceive message方法里就能拿到JS传过来的错误详情,你可以根据需求把这些信息上报、存储或者展示。

方案二:捕获evaluateJavaScript调用的错误

如果你只是需要捕获自己通过evaluateJavaScript执行的代码的错误,那可以优化你原来的代码,用异步回调或者Combine/Promise的方式,避免阻塞RunLoop:

extension WKWebView {
    func evaluateJavaScriptAsync(_ javaScriptString: String) async throws -> Any? {
        return try await withCheckedThrowingContinuation { continuation in
            evaluateJavaScript(javaScriptString) { result, error in
                if let error = error {
                    continuation.resume(throwing: error)
                } else {
                    continuation.resume(returning: result)
                }
            }
        }
    }
}

// 使用示例
Task {
    do {
        let result = try await webView.evaluateJavaScriptAsync("your JS code here")
        print("JS执行结果: \(result ?? "无返回值")")
    } catch {
        print("JS执行错误: \(error)")
    }
}

这种方式不会阻塞主线程,而且能准确捕获当前执行的JS代码的错误,但没法捕获网页中其他脚本的错误。


内容的提问来源于stack exchange,提问作者Konstantinos Natsios

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:10:58