Проблема с наблюдением изменений URL WKWebView через JavaScript событий - PullRequest
1 голос
/ 11 февраля 2020

У меня есть WKWebView, который представляет Laravel веб-приложение. В настоящее время у меня есть настройка ViewController для отслеживания изменений URL - и он ловит большинство из них. Однако есть небольшое количество изменений URL, которые вносятся с помощью событий JavaScript, которые WKWebView, похоже, игнорирует.

Я перепробовал все следующие решения и реализовал их в приведенном ниже коде:

Интересное замечание, прежде чем я представлю код: он способен обнаруживать простые изменения # ha sh, но невозможно обнаружить действие события JavaScript, которое загружается со страницы 1 на страницу 2 (это изменение URL, которое я пытаюсь отследить):

https://myweb.com/dashboardhttps://myweb.com/dashboard/projects/new

ViewController.swift

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {

    @IBOutlet var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView.navigationDelegate = self
        webView.uiDelegate = self

        webView.addObserver(self, forKeyPath: "URL", options: .new, context: nil)
        webView.addObserver(self, forKeyPath: #keyPath(WKWebView.url), options: .new, context: nil)

        webView.load(URLRequest(url: URL(string: "https://myweb.com/")!))
    }

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

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        let webURL = webView.url?.absoluteString
        let reqURL = navigationAction.request.url?.absoluteString
        print("webURL:", webURL)
        print("reqURL:", reqURL)
        decisionHandler(.allow)
    }

    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        let webURL = webView.url?.absoluteString
        print("webURL:", webURL)
    }
}

Существует один фрагмент кода, который Кажется, что t срабатывает при событии JavaScript, и это keyPath == #keyPath(WKWebView.url).

Однако, похоже, что он запускает только это, а также его вывод на консоль: URL Change 2: # No value provided (как следствие оператора слияния nil: self.webView.url?.absoluteString ?? "# No value provided".

Это означает, что эти 2 строки кода наблюдателя являются единственными вещами, которые улавливают это изменение, но не возвращают значение этого изменения:

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

if keyPath == #keyPath(WKWebView.url) {
    print("URL Change 2:", self.webView.url?.absoluteString ?? "# No value provided")
}

Любая другая попытка получить это измененное значение или принудительно развернуть его приводит к запуску cra * sh сразу после вызова didStartProvisionalNavigation:

Ошибка Xcode

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

Журнал консоли Xcode

Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/me/Apps/MyWeb/iOS/MyWeb/ViewController.swift, line 125
2020-02-11 12:47:55.169765-0500 MyWeb[3066:124221] Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/me/Apps/MyWeb/iOS/MyWeb/ViewController.swift, line 125
(lldb) 

Xcode Error & Console Log

Как я могу вывести значение этого изменения URL? Опять же, похоже, это единственный код, который срабатывает, когда я нажимаю кнопку JS для загрузки страницы (или, по крайней мере, так говорит консоль). В Safari как для iOS, так и для MacOS изменение URL-адреса отражается в строке поиска URL-адреса; таким образом, я не верю, что это проблема, связанная с веб-кодом (ie. frames, подмена URL).

Однако я не могу подтвердить это, поскольку это веб-скрипт PHP / JS.

Редактировать

Похоже, что при первой загрузке https://myweb.com в WKWebView, код также не обнаруживает первоначальное перенаправление на https://myweb.com/dashboard. Похоже, что только две строки наблюдателя, упомянутые выше, обнаруживают это перенаправление.

Редактировать 2

Я могу получить точное значение URL-адреса, выведенное на консоль с помощью кода функции ObserverValue:

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

Однако я не могу привести его как строку, присвоить значение переменной или передать его другой функции. Есть ли способ установить этот ключ как переменную, если он .contains("https://")? При запуске значение ключа = 0, но после этого значение = требуемый URL (https://myweb.com/dashboard и https://myweb.com/dashboard/project/new).


Это решение использует более старые методы KVO для достижения того же результат как @Rudedog, но с гораздо большим багажом (ie. Вам все еще нужно удалить наблюдателя и обработать пути независимых ключей). См. Ответ @ Rudedog ниже для более современного решения.

Редактировать 3 (решение KVO)

Мне удалось присвоить KVO WKWebView.url переменной String. Оттуда я смог передать значение String функции, которая затем обрабатывает каждый вывод, который я ищу:

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

1 Ответ

1 голос
/ 11 февраля 2020

Перед тем, как вы go продолжите, я настоятельно рекомендую вам использовать современный метод наблюдения Swift:

var webViewURLObserver: NSKeyValueObservation?

override func viewDidLoad() {
  // ...
  webViewURLObserver = webview.observe(\.url, options: .new) { webview, change in 
    print("URL: \(String(describing: change.newValue))"
    // The webview parameter is the webview whose url changed
    // The change parameter is a NSKeyvalueObservedChange
    // n.b.: you don't have to deregister the observation; 
    // this is handled automatically when webViewURLObserver is dealloced.
  }
}

Это делает ваш код намного чище и легче рассуждать, пока вы отладка проблемы.

...