通过JavaScript事件观察WKWebView URL更改的问题 [英] Issue with observing WKWebView URL changes via JavaScript events
问题描述
我有一个WKWebView,它显示一个 Laravel Web应用程序.目前,我有ViewController设置来捕获URL更改-它可以捕获大多数更改.但是,通过WKWebView似乎忽略的JavaScript事件进行的URL更改很少.
I have a WKWebView that presents a Laravel web application. I currently have my ViewController setup to catch URL changes – and it catches most of them. However, there are a small number of URL changes that are made via JavaScript events that WKWebView seems to ignore.
我尝试了以下所有解决方案,并在下面的代码中实现了它们:
I have tried all of the following solutions and implemented them in the code below:
- 如何在WKWebView中检测哈希变化?
- 我如何检测何时使用WKWebview更改amp页面的网址
- WKWebView函数,用于检测URL是否已更改
- 我如何检测何时使用WKWebview更改amp页面的网址
- 使用KVO观察WKWebView的URL属性在iOS 10中不起作用
- How to detect hash changes in WKWebView?
- How can I detect when url of amp page changed with WKWebview
- WKWebView function for detecting if the URL has changed
- How can I detect when url of amp page changed with WKWebview
- using KVO to observe WKWebView's URL property not work in iOS 10
在我展示代码之前,一个有趣的注释:它能够检测到简单的#hash更改,但是无法检测到从第1页加载到第2页的JavaScript事件操作(这是我尝试进行的URL更改)捕获):
An interesting note before I present the code: it is able to detect simple #hash changes, but is unable to detect a JavaScript event action that loads from page 1 to page 2 (this is the URL change I'm attempting to catch):
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)
}
}
似乎有一个代码片段可以触发JavaScript事件操作,即keyPath == #keyPath(WKWebView.url)
.
There is one single piece of code that seems to fire on the JavaScript event action, and that is keyPath == #keyPath(WKWebView.url)
.
但是,它似乎只是触发了它,以及它在控制台上显示的结果:URL Change 2: # No value provided
(由于nil合并运算符:self.webView.url?.absoluteString ?? "# No value provided"
的结果.
However, it only seems to fire just that, as well as its resulting print to console: URL Change 2: # No value provided
(as a consequence of the nil coalescing operator: self.webView.url?.absoluteString ?? "# No value provided"
.
这意味着,只有两行观察者代码可以捕获此更改,但不会返回该更改的值:
Meaning, these 2 lines of observer code are the only things that catch this change, but do not return the value of that change:
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")
}
任何其他尝试检索此更改的值或强制解开该值的操作,都会在调用didStartProvisionalNavigation
后立即导致启动崩溃:
Any other attempt made to retrieve this changed value, or force unwrap it, results in a crash on launch immediately after didStartProvisionalNavigation
is called:
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搜索栏中.因此,我认为这与基于Web的代码(即框架,URL欺骗)无关.
How am I able to output the value of that URL change? Again, it seems to be the only code that fires when I click the JS button to load the page (or at least, so says the console). In Safari, for both iOS and MacOS, the URL change does seem to reflect in the URL Search bar; thus, I don't believe this is an issue having to do with the web-based code (ie. frames, URL spoofing).
但是,由于它是基于Web的PHP/JS脚本,因此我无法确认.
However, I cannot confirm this as it is a web-based PHP/JS script.
看来,当第一次在WKWebView中加载https://myweb.com
时,该代码也没有检测到对https://myweb.com/dashboard
的初始重定向.上面提到的只有两条观察者行似乎可以检测到这种重定向.
It appears that, when first loading https://myweb.com
in the WKWebView, the code also does not detect the initial redirect to https://myweb.com/dashboard
. Only the two observer lines, mentioned above, seem to detect this redirect.
我能够使用ObserverValue函数代码将确切的URL值打印到控制台上:
I am able to get the exact URL value printed to the console with the observerValue function code:
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
).
However, I am unable to cast it as a String, assign the value to a variable or pass it to another function otherwise. Is there any way to set this key as a variable if it .contains("https://")
? On launch, the key value = 0
, but after that, the value = the desired URL (https://myweb.com/dashboard
and https://myweb.com/dashboard/project/new
).
此解决方案使用较旧的KVO方法获得与@Rudedog相同的结果,但负担更多(即,您仍然需要删除观察者并处理独立的键路径).有关更现代的解决方案,请参见下面的@Rudedog答案.
This solution uses older KVO methods to achieve the same result as @Rudedog, but with much more baggage (ie. you still need to remove the observer and handle independent key paths). See @Rudedog's answer below for a more modern solution.
我能够将KVO WKWebView.url
分配给String变量.从那里,我能够将String值传递给一个函数,该函数然后处理要查找的每个输出:
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
}
}
推荐答案
在继续之前,我强烈建议您使用现代的Swift观察方法:
Before you go any further, I would strongly recommend that you use the modern Swift method of observing:
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.
}
}
这样做可以使您的代码在调试问题时更清晰,更容易推理.
Doing this makes your code much cleaner and easier to reason about while you're debugging the issue.
这篇关于通过JavaScript事件观察WKWebView URL更改的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!