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

iOS Swift调用API下载PDF提示无法展示问题求解

错误原因

你看到的错误提示The document can not be shown翻译为无法显示该文档,是现有代码三个核心问题导致的:

  • 错误将二进制格式的PDF文件读取为String文本类型,得到的是完全无法被PDF解析器识别的乱码
  • URLSession下载任务返回的localURL指向系统临时目录,回调执行完毕后系统会自动删除该路径下的文件,你没有将文件移动到App沙盒的永久存储路径,后续访问时文件已被销毁
  • 缺少错误处理、响应合法性校验,且未主动将UI相关逻辑切回主线程执行,导致异常无法被捕获、UI操作时序混乱
修复步骤

1. 重写getPayroll方法

将回调改为返回文件保存结果,增加临时文件转存、错误校验逻辑:

func getPayroll(year: Int, month: Int, salaryType: Int, completion: @escaping (Result<URL, Error>) -> ()) {
    guard let url = URL(string: "https://rip.rtuk.gov.tr/workplacebackend/api/Employee/GetPayroll?yil=\(year)&ay=\(month)&maasturu=\(salaryType)") else {
        completion(.failure(NSError(domain: "PayrollDownload", code: -1, userInfo: [NSLocalizedDescriptionKey: "请求地址生成失败"])))
        return
    }

    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.addValue("Basic \(authToken)", forHTTPHeaderField: "Authorization")
    request.addValue(workingToken, forHTTPHeaderField: "token")

    let task = URLSession.shared.downloadTask(with: request) { localURL, response, error in
        // 处理网络层错误
        if let error = error {
            DispatchQueue.main.async {
                completion(.failure(error))
            }
            return
        }

        // 校验响应合法性:状态码200、返回类型为PDF、临时文件存在
        guard let httpResp = response as? HTTPURLResponse,
              httpResp.statusCode == 200,
              httpResp.mimeType == "application/pdf",
              let tempURL = localURL else {
            let invalidErr = NSError(domain: "PayrollDownload", code: -2, userInfo: [NSLocalizedDescriptionKey: "服务器返回内容不是合法PDF文件"])
            DispatchQueue.main.async {
                completion(.failure(invalidErr))
            }
            return
        }

        // 将临时文件移动到沙盒缓存目录永久保存
        do {
            let cacheDir = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            let fileName = "薪资单_\(year)年\(month)月_type\(salaryType).pdf"
            let saveURL = cacheDir.appendingPathComponent(fileName)
            // 存在同名文件先删除,避免移动失败
            if FileManager.default.fileExists(atPath: saveURL.path) {
                try FileManager.default.removeItem(at: saveURL)
            }
            try FileManager.default.moveItem(at: tempURL, to: saveURL)
            DispatchQueue.main.async {
                completion(.success(saveURL))
            }
        } catch {
            DispatchQueue.main.async {
                completion(.failure(error))
            }
        }
    }
    task.resume()
}

2. 修改列表点击逻辑

增加下载中提示,处理下载成功/失败分支,下载完成后直接打开PDF:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    switch cellType(rawValue: selected.title) {
    case .salary:
        selectedPayment = payment![indexPath.row]
        // 弹出下载中提示
        let loadingAlert = UIAlertController(title: nil, message: "正在下载薪资单...", preferredStyle: .alert)
        present(loadingAlert, animated: true)

        RtukNetworkManager.shared.getPayroll(
            year: selectedPayment.year,
            month: selectedPayment.month,
            salaryType: selectedPayment.paymentType
        ) { [weak self] result in
            loadingAlert.dismiss(animated: true) {
                switch result {
                case .success(let fileURL):
                    // 下载成功,打开PDF
                    self?.showPDF(fileURL: fileURL)
                case .failure(let err):
                    // 下载失败弹出提示
                    let errAlert = UIAlertController(title: "下载失败", message: err.localizedDescription, preferredStyle: .alert)
                    errAlert.addAction(UIAlertAction(title: "确定", style: .default))
                    self?.present(errAlert, animated: true)
                }
            }
        }
    default: break
    }
}

// PDF打开方法
func showPDF(fileURL: URL) {
    guard FileManager.default.fileExists(atPath: fileURL.path) else {
        let alert = UIAlertController(title: "错误", message: "文件不存在", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "确定", style: .default))
        present(alert, animated: true)
        return
    }
    let docController = UIDocumentInteractionController(url: fileURL)
    docController.delegate = self
    docController.presentPreview(animated: true)
}

// 实现文档预览代理
extension YourViewController: UIDocumentInteractionControllerDelegate {
    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
        return self
    }
}
注意事项
  • 原有downloadFile方法如果是接收远程URL下载的逻辑可以直接废弃,现在下载逻辑已经在网络层完成,直接传入本地沙盒路径打开即可
  • 如果需要自定义下载存储路径,可以把代码里的.cachesDirectory换成.documentDirectory,注意大文件不要存在Documents目录避免iCloud同步被拒
  • 所有UI操作必须放在主线程执行,避免出现无响应、弹窗时序错误问题

内容的提问来源于stack exchange,提问作者Mert Köksal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:45:30