У меня есть WKWebView, который представляет Laravel веб-приложение. В настоящее время у меня есть настройка ViewController для отслеживания изменений URL - и он ловит большинство из них. Однако есть небольшое количество изменений URL, которые вносятся с помощью событий JavaScript, которые WKWebView, похоже, игнорирует.
Я перепробовал все следующие решения и реализовал их в приведенном ниже коде:
Интересное замечание, прежде чем я представлю код: он способен обнаруживать простые изменения # ha sh, но невозможно обнаружить действие события JavaScript, которое загружается со страницы 1 на страницу 2 (это изменение URL, которое я пытаюсь отследить):
https://myweb.com/dashboard
→ https://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)
Как я могу вывести значение этого изменения 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
}
}