Vicente Garcia
Vicente Garcia

Reputation: 6380

Fatal error while using evaluateJavaScript on WKWebView

WKWebView crashes while trying to evaluate JavaScript on Xcode 14.1, using Swift, tested on iOS but same behaviour should be on macOS.

I made a vastly simplified example to try to find a solution, and it keeps crashing:

let webView = WKWebView()
Task {
    try? await webView.evaluateJavaScript("console.log('hello world')")
}

:0: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

Upvotes: 11

Views: 4596

Answers (3)

Nipun
Nipun

Reputation: 634

just delete this block in InAppWebView

public override func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil) {
    if let applePayAPIEnabled = settings?.applePayAPIEnabled, applePayAPIEnabled {
        if let completionHandler = completionHandler {
            completionHandler(nil, nil)
        }
        return
    }
    super.evaluateJavaScript(javaScriptString, completionHandler: completionHandler)
}

enter image description here

Upvotes: 0

arnaldogn
arnaldogn

Reputation: 61

another option could be something like:

@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()
            }
        }
    }
}

Upvotes: 6

Vicente Garcia
Vicente Garcia

Reputation: 6380

Seems that part of the problem is method overload, as of Xcode 14.1 there are several methods named evaluateJavaScript as part of WKWebView.

Due to optional parameters they seem to have the same signature, and the compiler is having a hard time understanding which one we mean.

Methods

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?

After testing different scenarios it seems that when using async/await version of these methods WKWebView expects JavaScript to return with a value (something other than Void), if there is no value returning from the JavaScript that you evaluate you will have a crash.

Solution

Option 1

Always make sure JavaScript returns a value.

Crashing:

try? await webView.evaluateJavaScript("console.log('hello world')") // fatal error

Not crashing:

try? await webView.evaluateJavaScript("console.log('hello world'); 0")

Option 2

When not possible to return a value explicitly use the signature with a completion handler (even if you pass nil as the handler).

webView.evaluateJavaScript("console.log('hello world')", completionHandler: nil)

Upvotes: 23

Related Questions