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
相关产品推荐
相关产品推荐

