WKWebView的decidePolicyFor加载后触发,如何提前拦截待加载URL?
我来帮你搞定这个头疼的问题!你说的这个情况确实不符合decidePolicyFor navigationAction的设计初衷——这个方法本来就该在请求发起前执行,用来决定是否允许加载。咱们一步步排查解决:
1. 先检查代理是否正确设置
这是最容易忽略的点!你必须确保在WKWebView开始加载任何内容之前,就把它的navigationDelegate设置为当前ViewController。比如初始化WebView的代码应该是这样的:
override func viewDidLoad() { super.viewDidLoad() let config = WKWebViewConfiguration() let webView = WKWebView(frame: view.bounds, configuration: config) // 关键:先设置代理,再加载内容 webView.navigationDelegate = self view.addSubview(webView) // 之后再调用loadRequest webView.load(URLRequest(url: yourTargetURL)) }
如果是在加载之后才设置代理,WebView已经开始请求内容了,代理方法自然会延迟触发,导致禁限URL先显示出来。
2. 确认matches(url:)是同步方法
如果你的matches方法是异步执行的(比如内部用了网络请求、异步正则匹配这类操作),那会导致decisionHandler不能立即被调用——WKWebView会在等待你返回决策的间隙,先开始加载内容。
你要保证matches(url:)是同步完成判断的,比如用同步的正则匹配逻辑,这样能立刻得到结果并调用decisionHandler,WebView就不会提前加载了。
3. 处理缓存带来的“提前显示”问题
如果这个禁限URL之前被加载过,WebView可能会直接从本地缓存读取内容展示,哪怕你后续拦截了请求。解决办法有两个:
- 在加载URL时,设置请求的缓存策略为忽略本地缓存:
var request = URLRequest(url: targetURL) request.cachePolicy = .reloadIgnoringLocalCacheData webView.load(request) - 当拦截到禁限URL时,手动清除该URL的缓存:
if forbiddenUrlPattern.matches(url: navigationURL) { // 清除缓存 URLCache.shared.removeCachedResponse(for: navigationAction.request) decisionHandler(.cancel) showFullScreenError(error: .forbidden) return }
4. 提前拦截:在调用loadRequest前先判断
如果上面的方法都不行,咱们可以换个思路——在WebView开始加载前,先手动判断URL是否合法,从根源上避免加载禁限内容:
func safeLoadURL(_ url: URL) { let forbiddenPattern = Configuration.current.links.forbiddenUrlPattern guard !forbiddenPattern.matches(url: url) else { showFullScreenError(error: .forbidden) return } webView.load(URLRequest(url: url)) }
之后所有加载URL的操作都通过这个方法来调用,就能彻底避免“先显示再拦截”的问题。
最后排查Xcode版本问题
Xcode 11.2.1确实存在一些WKWebView的小bug,如果你有条件的话,可以尝试升级到更稳定的版本(比如Xcode 11.4+),说不定能解决这个代理延迟的问题。
内容的提问来源于stack exchange,提问作者nfarshchi

