You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:14:37