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

SwiftUI中通过URL分享Firestore PDF报错的修复及优化建议

问题修复与优化方案

错误原因分析与修复

1. 主线程执行耗时操作错误

你的代码中PDFDocument(url: url)和dataRepresentation()属于磁盘/网络IO操作,直接在主线程执行会阻塞UI,触发"不应在主线程调用"的警告。

修复方式:将这些耗时操作移到后台队列执行,完成后切回主线程处理UI逻辑。

2. RBSServiceErrorDomain权限错误

该错误源于手动通过UIApplication.shared获取根视图控制器的方式不符合iOS权限规范,同时直接分享内存中的Data对象,部分分享活动不支持这种形式。

修复方式:改用SwiftUI原生的.sheet展示分享控制器,并将PDF保存到本地临时目录后分享本地文件URL。

优化后的完整代码

import SwiftUI
import PDFKit
import UniformTypeIdentifiers

struct PresentPDF: View {
    let data: YourDataModel // 替换为你的实际数据模型类型
    @State private var isShareSheetPresented = false
    @State private var sharedFileURL: URL?
    
    var body: some View {
        VStack {
            if let url = URL(string: data.pdfUrl ?? "") {
                PDFKitView(url: url)
            }
        }
        .toolbar {
            ToolbarItemGroup {
                Button(action: prepareForShare) {
                    Label("Share", systemImage: "square.and.arrow.up")
                }
            }
        }
        .sheet(isPresented: $isShareSheetPresented) {
            if let url = sharedFileURL {
                ShareSheet(activityItems: [url])
            }
        }
    }
    
    // 后台线程处理PDF读取与本地保存
    private func prepareForShare() {
        guard let pdfUrlString = data.pdfUrl, let remoteUrl = URL(string: pdfUrlString) else {
            // 处理URL无效的情况
            return
        }
        
        DispatchQueue.global(qos: .background).async {
            guard let pdfDocument = PDFDocument(url: remoteUrl),
                  let pdfData = pdfDocument.dataRepresentation() else {
                // 处理PDF读取失败的情况
                return
            }
            
            // 将PDF保存到系统临时目录
            let tempDir = FileManager.default.temporaryDirectory
            let fileName = "shared_pdf_\(UUID().uuidString).pdf"
            let localFileUrl = tempDir.appendingPathComponent(fileName)
            
            do {
                try pdfData.write(to: localFileUrl)
                DispatchQueue.main.async {
                    self.sharedFileURL = localFileUrl
                    self.isShareSheetPresented = true
                }
            } catch {
                print("保存PDF失败: \(error.localizedDescription)")
            }
        }
    }
}

// SwiftUI封装的分享控制器
struct ShareSheet: UIViewControllerRepresentable {
    let activityItems: [Any]
    let applicationActivities: [UIActivity]? = nil
    
    func makeUIViewController(context: Context) -> UIActivityViewController {
        UIActivityViewController(activityItems: activityItems, applicationActivities: applicationActivities)
    }
    
    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

// PDFKitView实现示例
struct PDFKitView: UIViewRepresentable {
    let url: URL
    
    func makeUIView(context: Context) -> PDFView {
        let pdfView = PDFView()
        pdfView.document = PDFDocument(url: url)
        pdfView.autoScales = true
        return pdfView
    }
    
    func updateUIView(_ uiView: PDFView, context: Context) {}
}

关键优化点说明

  • 后台线程隔离:把PDF读取、文件写入等耗时操作放到后台队列,避免阻塞主线程,解决UI无响应警告。
  • 本地URL分享:将PDF保存到临时目录后分享本地文件URL,兼容更多分享活动,同时规避权限错误。
  • SwiftUI原生范式:用.sheet和UIViewControllerRepresentable封装分享控制器,符合SwiftUI设计逻辑,避免手动操作UIKit层级的风险。
  • 安全的可选值处理:移除所有强制解包(!),用可选绑定处理可能的空值,提升代码稳定性。
  • 自动文件清理:使用系统临时目录存储分享文件,系统会自动清理,无需手动管理文件生命周期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 09:25:30