0

I've posted a more in-depth question to try and get to the bottom of the issue, but in a brief:

I'm attempting to show a PHP/JS-based web application (Laravel) through a WKWebView. However, due to the nature of the script's redirecting properties, the only code I've gotten to actually detect the URL change is with #keyPath(WKWebView.url):

override func viewDidLoad() {
    super.viewDidLoad()

    webView.navigationDelegate = self
    webView.uiDelegate = self    
    webView.addObserver(self, forKeyPath: #keyPath(WKWebView.url), options: .new, context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == #keyPath(WKWebView.url) {
        print("URL Change:", self.webView.url?.absoluteString ?? "# No value provided")
    }
}

However, the output to console is always the same:

URL Change: # No value provided

So I know that the KVO for WKWebView.url is able to fire upon script-based redirection within the WebView. In fact, if you take a look at my other question, it is the only code that can detect this sort of redirection – which is strange, because when launched in Safari (both iOS and macOS), the URL bar is able to reflect those redirected changes to the URL's value. However, when in the WKWebView, none of the WKNavigationDelegate functions are able to detect such a change to the URL.

Is there any way to obtain the URL directly from the keyPath value of WKWebView.url when fired? Are there any alternatives, not described in my previously-mentioned question, that could obtain the URL?

Trying to obtain the URL value from webView.url seems to always return nil.

EDIT: I am able to get the exact URL value with the observerValue function code:

if let key = change?[NSKeyValueChangeKey.newKey] {
    print("URL: \(key)") // url value
}

However, I am unable to cast it as a String or pass it to another function otherwise. Is there any way to set this key as a variable, if it .contains("https://")?

JDev
  • 5,168
  • 6
  • 40
  • 61

1 Answers1

1

I was able to assign the KVO WKWebView.url to a String variable. From there, I was able to pass the String value to a function that then handles each output I'm looking for:

var cURL = ""

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if let key = change?[NSKeyValueChangeKey.newKey] {
        cURL = "\(key)"         // Assign key-value to String
        print("cURL:", cURL)    // Print key-value
        cURLChange(url: cURL)   // Pass key-value to function
    }
}

func cURLChange(url: String) {
    if cURL.contains("/projects/new") {
        print("User launched new project view")
        // Do something
    } else {
        // Do something else
    }
}

A similar solution, using a more modern method (with less hassle), was provided here.

var cURL = ""
var webView: WKWebView!
var webViewURLObserver: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()

    // 1. Assign changed value to variable
    webViewURLObserver = webView.observe(\.url, options: .new) { webView, change in
        self.cURL = "\(String(describing: change.newValue))" }

    // 2. Print value of WKWebView URL
    webViewURLObserver = webView.observe(\.url, options: .new) { webView, change in
        print("URL: \(String(describing: change.newValue))"
    )}

By using NSKeyValueObservation of an object, you don't need to remove observers or check observer values by keyPath. You can simply set it to observe an object (ie. WKWebView) and run code when a change is observed.

JDev
  • 5,168
  • 6
  • 40
  • 61