WKWebView
尝试在 Xcode 14.1 上使用 Swift 评估 JavaScript 时崩溃,在 iOS 上进行了测试,但在 macOS 上应该有相同的行为。
我做了一个非常简化的示例来尝试找到解决方案,但它不断崩溃:
let webView = WKWebView()
Task {
try? await webView.evaluateJavaScript("console.log('hello world')")
}
:0:致命错误:隐式解包时意外发现 nil 可选值
问题的一部分似乎是方法重载,从 Xcode 14.1 开始,有几个名为
evaluateJavaScript
的方法作为 WKWebView
的一部分。
由于可选参数,它们似乎具有相同的签名,并且编译器很难理解我们的意思。
open func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)
open func evaluateJavaScript(_ javaScriptString: String) async throws -> Any
@MainActor public func evaluateJavaScript(_ javaScript: String, in frame: WKFrameInfo? = nil, in contentWorld: WKContentWorld, completionHandler: ((Result<Any, Error>) -> Void)? = nil)
@MainActor public func evaluateJavaScript(_ javaScript: String, in frame: WKFrameInfo? = nil, contentWorld: WKContentWorld) async throws -> Any?
在测试了不同的场景之后,似乎当使用这些方法的
async/await
版本时,WKWebView
期望JavaScript返回一个值(Void
以外的值),如果您评估的JavaScript没有返回值,您将发生车祸。
始终确保 JavaScript 返回一个值。
崩溃:
try? await webView.evaluateJavaScript("console.log('hello world')") // fatal error
不崩溃:
try? await webView.evaluateJavaScript("console.log('hello world'); 0")
当无法显式返回值时,请使用具有完成处理程序的签名(即使您传递 nil 作为处理程序)。
webView.evaluateJavaScript("console.log('hello world')", completionHandler: nil)
另一种选择可能是这样的:
@MainActor
func evaluateJavaScript(string javaScriptString: String) async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
evaluateJavaScript(javaScriptString) { _, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume()
}
}
}
}