11

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

3 Answers 3

23

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)
Sign up to request clarification or add additional context in comments.

3 Comments

Wow, I'd suggest filing a bug on this one!
Good call @matt. I just did that: FB11770227
wow, thanks you so much for this solution :D could have spent way more time on this but luckily I found this thread, although it is not that easy to find.
6

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.