iOS WebKit - Determine whether a WebView has completely loaded JS content

Viewed 1299

I'm attempting to parse out some HTML metadata from a third-party URL; however, I don't get the full HTML data until some Javascript scripts are executed in the WKWebView.

So I've been trying to detect when the WKWebView has completely loaded the web page with Javascript but unfortunately I wasn't able to.

Here's what I tried:

  • I tried using WKUserContentController and handled the protocol method but that didn't work because the protocol method is only executed when it receives a script message not when it's completed.
  • I tried adding an observer for WKWebView's estimatedProgress but estimatedProgress return 1.0 before JS scripts are done loading.
  • I tried using WKNavigationDelegate protocol method webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) but it's essentially the same thing as adding an observer.
  • I also tried using WKWebView's evaluateJavaScript method in the observeValue method to check both:
    • document.readyState == \"interactive\"
    • document.readyState == \"completed\"

but that didn't work either because it's executed way before JS is done loading.

Code

Here's the code of the last approach I attempted and didn't work:

Initializing the WKWebView

        let webView = WKWebView(frame: self.view.frame)
        webView.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
        webView.load(URLRequest(url: url))
        webView.navigationDelegate = self
        self.view.addSubview(webView)

Handling the observer

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "estimatedProgress" {
        if let webView = object as? WKWebView, webView.estimatedProgress > 0.5 {
            print(webView.estimatedProgress)
            webView.evaluateJavaScript("document.readyState == \"interactive\"") { (isLoaded, err) in
                print(isLoaded)
                if let loaded = isLoaded as? Bool, loaded == true {
                    print("completely done loading with JS")
                }
            }
        }
    }
}

The result I get when the evaluateJavaScript method is executed is false because it's executed too early. But I'm trying to let the observer continue until isLoaded is true.

1 Answers

To check if your WKWebView has loaded:

import WebKit
import UIKit

class ViewController: UIViewController, WKNavigationDelegate {

  let webView = WKWebView()

  override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "https://www.yourwebsite.com/") !
    let request = URLRequest(url: url)
    webView.navigationDelegate = self
    webView.load(request)
  }

func webView(_ webView: WKWebView, didStart navigation: WKNavigation!) {
    print("Strat to load")
}

  func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
     print("loaded")
     //It will print "loaded" in the console everytime the WKWebView has finished loading the page. 
  }
}

But it is per each resource, not for the whole page with all the resources. Take a look at https://stackoverflow.com/a/43172313/3857830

Related