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

WKWebView中PDF下载失效 下载代理方法未触发问题

WKWebView 无法触发PDF下载回调、报WebKit错误102的问题

问题描述

使用WKWebView加载网页时,站点内点击按钮生成PDF并触发下载,已实现导航、下载相关代理方法,但运行时仅webView(_:decidePolicyFor:preferences:decisionHandler:)方法被调用,且navigationAction.shouldPerformDownload始终为false,其余下载相关代理均不触发,控制台输出错误:

WebPageProxy::didFailProvisionalLoadForFrame: frameID=3, domain=WebKitErrorDomain, code=102, isMainFrame=1

已实现的核心代码如下:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
    
    if navigationAction.shouldPerformDownload {
        decisionHandler(.download, preferences)
    } else {
        decisionHandler(.allow, preferences)
    }
}

func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
    download.delegate = self
}

func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
    download.delegate = self
}

func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) {
    
    if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
        let url = dir.appendingPathComponent(suggestedFilename)
        completionHandler(url)
    }
    
}

根因分析

错误码102对应WebKit错误WebKitErrorFrameLoadInterruptedByPolicyChange,即帧加载被策略变更中断,结合回调表现,可能原因如下:

  • 仅在请求发起阶段判断下载标识,未处理响应阶段的下载识别:navigationAction.shouldPerformDownload仅在请求发起阶段就能明确判定为下载时才会返回true,如果服务端未在请求阶段给出下载标识、仅在响应头返回Content-Disposition: attachment,或者返回的MIME类型是WKWebView无法渲染的二进制类型,请求阶段无法识别为下载,允许导航后就会因为内容无法渲染触发加载中断。
  • 网页下载逻辑由前端JS实现,不走原生导航流程:如果按钮点击后是通过fetch/XHR拉取PDF二进制数据,再通过Blob+动态a标签的方式触发前端下载,这类操作不会触发WKWebView的原生导航下载逻辑,shouldPerformDownload始终为false,最终因为Blob URL无法被主帧加载触发102错误。
  • 跨域请求被拦截:如果PDF生成接口和当前网页主域不一致,且服务端未配置合法的CORS响应头,WKWebView会拦截跨域请求,直接触发预加载失败,不会进入后续下载流程。
  • 下载代理回调实现不全:当前未实现decidePolicyFor navigationResponse的导航响应代理方法,无法在响应阶段主动将无法渲染的内容标记为下载,也未实现下载失败回调,无法捕获下载流程中的具体错误。

修复方案

  • 补全导航响应阶段的下载判断逻辑,不要仅依赖系统在请求阶段的自动识别:
    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        // 无法直接渲染的MIME类型,标记为下载
        guard navigationResponse.canShowMIMEType else {
            decisionHandler(.download)
            return
        }
        // 响应头标记为附件的内容,标记为下载
        if let httpResponse = navigationResponse.response as? HTTPURLResponse,
           let contentDisposition = httpResponse.allHeaderFields["Content-Disposition"] as? String,
           contentDisposition.lowercased().contains("attachment") {
            decisionHandler(.download)
            return
        }
        decisionHandler(.allow)
    }
    
  • 针对前端Blob类型的下载,需要注入JS脚本拦截前端的Blob、a标签下载行为,通过JSBridge将文件数据传递给原生层,由原生完成本地文件写入和系统下载调度,这类纯前端触发的下载不会走WKWebView原生下载回调。
  • 检查PDF生成接口的跨域配置,确保接口返回合法的Access-Control-Allow-Origin等CORS头,避免跨域拦截导致的请求失败。
  • 补全WKDownload的失败代理回调,捕获下载阶段的具体错误便于排查:
    func download(_ download: WKDownload, didFailWithError error: Error, resumeData: Data?) {
        print("下载失败:\(error.localizedDescription)")
    }
    

内容的提问来源于stack exchange,提问作者Ginso

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 05:39:41