Andrey Chernukha Andrey Chernukha - 18 days ago 4
Swift Question

WKWebView. didStartProvisionalNavigation not called

I'm using

WKWebView
in my app and everything works fine until user is going to money.cnn.com. For the main page of this site everything is fine as well but when user leaves the main page and goes for example to http://money.cnn.com/2016/11/21/news/dubai-trump-golf-course-developer/index.html?iid=hp-toplead-intl or any other page on this site the delegate methods
didStartProvisionalNavigation
and
didCommitNavigation
not called. But I need these methods in order to detect the start of a navigation.
decidePolicyForNavigationAction
is called at the very beginning however it is called multiple times and I feel that using this method is not a perfect solution. So my question is: what can I do in such a situation? What so especial can be at that site what doesn't make
WKWebView
call its delegate methods and what may be a reliable solution in this case? Thanks

Answer

I do not know very well web development, but it seems to me that the problem with JavaScript of the money.cnn.com site. It seems that the transitions between the site pages and their loading is carried out with help of JavaScript. That JavaScript conflict with native WKWebView handlers.

But! You can inject your own JavaScript with handler at the beginning of the page. And when the page begins to open, the script will work first.

Full example code

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {

    var webView = WKWebView()
    let scriptName = "GetUrl"

    override func viewDidLoad() {
        super.viewDidLoad()

        let contentController = WKUserContentController();
        let script = "webkit.messageHandlers.\(scriptName).postMessage(document.URL)"
        let userScript = WKUserScript(source: script, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: true)
        contentController.addUserScript(userScript)
        contentController.add(self, name: scriptName)

        let config = WKWebViewConfiguration()
        config.userContentController = contentController

        webView = WKWebView(frame:  UIScreen.main.bounds, configuration: config)
        webView.navigationDelegate = self
        view.addSubview(webView)
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == scriptName {
            if let url = message.body as? String {
                if url.contains("http://money.cnn.com/") {
                    print("!!! \(url)")
                }
            }
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        loadUrl(string: "http://money.cnn.com/2016/11/21/news/dubai-trump-golf-course-developer/index.html?iid=hp-toplead-intl")
    }

    func loadUrl(string: String) {
        if let url = URL(string: string) {
            webView.load(URLRequest(url: url))
        }
    }
}

Info.plist

add in your Info.plist transport security setting

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

or

enter image description here

Result

enter image description here