如何在iOS WKWebView中实现类似Android shouldIntercept的全请求拦截?
iOS WKWebView 实现类似 Android shouldInterceptRequest 的全请求拦截
没问题!iOS 的 WKWebView 完全能实现类似 Android WebView 中 shouldInterceptRequest 的全请求拦截能力——不管是资源、图片、视频、HTML 还是 JavaScript 这类内容,都能拦下来自定义处理。下面给你拆解两种实用方案,还有关键注意事项:
推荐方案:使用 WKURLSchemeHandler(iOS 11+)
这是苹果官方主推的拦截方案,虽然它默认是给自定义 URL Scheme 用的,但咱可以绕个小弯来拦截标准的 HTTP/HTTPS 请求:
- 第一步:先注册一个自定义 Scheme(比如叫
myapp),然后在WKNavigationDelegate的decidePolicyForNavigationAction方法里,把所有 HTTP/HTTPS 请求重定向到这个自定义 Scheme。举个例子,把https://example.com/image.png改成myapp://https/example.com/image.png。 - 第二步:实现
WKURLSchemeHandler协议的两个核心方法:webView:startURLSchemeTask::当 WebView 发起自定义 Scheme 请求时,这个方法会被触发。你可以在这里做任何自定义操作——比如从本地缓存取资源、修改请求头、重新发起网络请求等等,最后通过WKURLSchemeTask的回调方法把处理好的数据返回给 WebView。webView:stopURLSchemeTask::用来处理请求被取消的场景,比如用户中途关掉页面,你可以在这里取消对应的网络任务。
这个方案的优势是能完全掌控请求的生命周期,和 Android 的 shouldInterceptRequest 能力几乎一模一样,所有类型的请求都能覆盖到。
兼容方案:结合 WKNavigationDelegate 与 NSURLSession(iOS 8+)
如果你的 App 需要兼容 iOS 11 以下的版本,可以用这种组合方式:
- 在
WKNavigationDelegate的decidePolicyForNavigationAction里,先判断要拦截的请求,然后调用decisionHandler(.cancel)取消 WebView 的默认加载。 - 自己用
NSURLSession发起这个请求,在回调里处理响应——比如替换成本地资源、修改返回的 HTML/JS 内容。 - 处理完成后,调用 WKWebView 的
loadData:MIMEType:characterEncodingName:baseURL:方法,把处理好的内容加载进去。
注意:这种方式处理主文档(比如 HTML)很顺手,但对于图片、JS、CSS 这类子资源,得逐个拦截处理,相对繁琐一些。
几个关键坑要避开
- HTTPS 请求的证书问题:如果是自定义证书,得在
NSURLSessionDelegate的didReceiveChallenge方法里手动信任证书,不然请求会失败。 - 正确设置 MIME 类型:返回给 WebView 的内容要对应正确的 MIME 类型,比如图片用
image/png,JS 用application/javascript,不然 WebView 可能解析不了内容。 - AJAX 请求拦截:如果要拦 WebView 内部的 AJAX 请求,
WKURLSchemeHandler方案会更靠谱,它能捕获所有类型的请求,包括 XMLHttpRequest 发起的请求。
极简的 WKURLSchemeHandler 实现示例
class CustomSchemeHandler: NSObject, WKURLSchemeHandler { func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { // 把自定义Scheme的URL还原成原始的HTTP/HTTPS地址 guard let originalUrlStr = urlSchemeTask.request.url?.absoluteString.replacingOccurrences(of: "myapp://", with: "https://"), let originalURL = URL(string: originalUrlStr) else { urlSchemeTask.didFailWithError(NSError(domain: "CustomSchemeError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效URL"])) return } var customRequest = URLRequest(url: originalURL) // 这里可以修改请求头、添加参数等操作 let task = URLSession.shared.dataTask(with: customRequest) { data, response, error in if let error = error { urlSchemeTask.didFailWithError(error) return } guard let validResponse = response, let validData = data else { urlSchemeTask.didFailWithError(NSError(domain: "CustomSchemeError", code: -2, userInfo: [NSLocalizedDescriptionKey: "无响应或数据"])) return } // 必须回到主线程回调WebView DispatchQueue.main.async { urlSchemeTask.didReceive(validResponse) urlSchemeTask.didReceive(validData) urlSchemeTask.didFinish() } } task.resume() } func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) { // 如果保存了task的引用,这里可以调用task.cancel()取消任务 } }
初始化 WKWebView 时注册这个 handler:
let webConfig = WKWebViewConfiguration() webConfig.setURLSchemeHandler(CustomSchemeHandler(), forURLScheme: "myapp") let webView = WKWebView(frame: .zero, configuration: webConfig)
在 WKNavigationDelegate 里做请求重定向:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard let url = navigationAction.request.url, let scheme = url.scheme?.lowercased(), (scheme == "http" || scheme == "https") else { decisionHandler(.allow) return } // 把HTTP/HTTPS请求转成自定义Scheme的请求 let customUrlStr = url.absoluteString.replacingOccurrences(of: "\(scheme)://", with: "myapp://\(scheme)/") guard let customURL = URL(string: customUrlStr) else { decisionHandler(.allow) return } webView.load(URLRequest(url: customURL)) decisionHandler(.cancel) }
这样一套下来,就能实现和 Android shouldInterceptRequest 几乎一致的全请求拦截能力了。
内容的提问来源于stack exchange,提问作者casolorz
相关产品推荐
相关产品推荐

