1

I am trying to run a javascript command that works if I run it in the console on the web. I have tried doing the code below, but I keep getting nil as my response.

webView.evaluateJavaScript("command") { (result, error) in
     if error == nil {
          print(result)
     } else {
          print(error)
     }
}
4
  • For correction if error != nil { print(result) } it means the is an error ---> There is no result then. Commented Aug 10, 2020 at 2:51
  • I fixed it and this is the error that I get: Error Domain=WKErrorDomain Code=4 "A JavaScript exception occurred" UserInfo={WKJavaScriptExceptionLineNumber=0, WKJavaScriptExceptionMessage=TypeError: undefined is not an object, WKJavaScriptExceptionColumnNumber=0, NSLocalizedDescription=A JavaScript exception occurred I just want to run a command that I know for sure works when I put it in the javascript console in chrome on my computer. Is this possible? Commented Aug 10, 2020 at 3:00
  • Test your command directly in WKWebView. You can attach a desktop Safari’s web inspector to the web view that is currently opened in the iOS simulator. Commented Aug 10, 2020 at 6:32
  • A sample javascript command you could try is (() => { return 5 })(); and see if you get a 5 for result on iOS. The javascript just creates and runs a function that returns the value 5. Commented Aug 20, 2020 at 3:07

2 Answers 2

2

In case if your webview loads a part of body(or whole html body) additionally, then you won't have desired results by evaluating your javascript inside of method:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) 

Reason for this is because "didFinish" method is called once navigation is done inside of webview, but not strictly after loading whole content inside of webView which could be in states:

  1. state: empty body
  2. state: some loading
  3. state: javascript framework did his job and inserted app-body inside
  4. state: body has content

"didFinish" is called in state 1. You can wait for some time with DispatchQueue.main.asyncAfter and then do the .evaluateJavascript but it will make webView makes changes in front of user(ugly per my opinion...)

My proposal would be this extension:

extension WKWebView {

// it re-execute javascript in cases when body is attached additionally on the html by some inner javascript framework
// it tries around 50 times maximum in time range of 10 sec
// if body is not loaded in 10 secs, this won't work
func rexecuteJavascript(tryouts: Int = 0, script: String) {
    self.evaluateJavaScript(script, completionHandler: { (result: Any?, error: Error?) in
        if let _ = error, tryouts < 50 {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
                return self.rexecuteJavascript(tryouts: tryouts + 1, script: script)
            }
        }
    })
}

}

It works perfectly for me because body of webview gets populated in around 2-3 secs after "didFinish", and I use this to execute my javascript on fully loaded body:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    let removeFooterScript = "document.getElementsByClassName('global-footer')[0].style.display='none';"

    webView.rexecuteJavascript(script: removeFooterScript)
}

Cheers!

Sign up to request clarification or add additional context in comments.

Comments

0

Here is the code to inject javascript code in your WKWebView

let wkUScript = WKUserScript(source: "your javascript command",
                             injectionTime: .atDocumentEnd,
                             forMainFrameOnly: true)
let wkUController = WKUserContentController()
wkUController.addUserScript(wkUScript)

let wkWebConfig = WKWebViewConfiguration()
wkWebConfig.userContentController = wkUController

let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 400, height: 400), configuration: wkWebConfig)

2 Comments

How would you print out the response?
You set webView.UIDelegate = self then call this delegate: func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { print(message) }

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.