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

SwiftUI使用DocumentPicker拷贝文件到应用报错Generation not found

SwiftUI DocumentPicker 文件导入"Generation not found"报错修复

问题场景

在iPhone端基于SwiftUI开发时,通过DocumentPicker唤起系统文件应用,引导用户选择图片/文件导入到应用沙盒,初始化UIDocumentPickerViewController时已经设置asCopy: true,但在代理回调读取选中文件URL内容时出现如下报错:

Error Domain=GSLibraryErrorDomain Code=3 "Generation not found" UserInfo={NSDescription=Generation not found}

操作演示:
操作演示

报错核心原因

  • 即使设置了asCopy: true,系统回调返回的URL是带安全作用域的临时URL,回调触发时跨进程文件拷贝可能还未完成,且未主动申请安全域访问权限就直接读取,会触发资源不存在的错误
  • 原代码直接用String(contentsOf:encoding:)读取所有选中文件,但配置支持的PDF、图片、二进制文件都不是UTF8文本编码,强行读取文本本身就会失败
  • 原代码没有主动将文件拷贝到应用自身沙盒的持久化目录,系统返回的临时URL会在回调结束后被系统回收权限,异步访问或者后续重启App都会失效
  • 原代码配置了.folder类型,但没有实现文件夹遍历逻辑,直接读取文件夹URL必然失败

正确实现代码

import SwiftUI
import UniformTypeIdentifiers

// 调用端示例
struct DemoView: View {
    @State private var showDocumentPicker = false
    @State private var pickedFileData: Data?
    @State private var pickedTextContent: String?

    var body: some View {
        Button("选择导入文件") {
            showDocumentPicker = true
        }
        .sheet(isPresented: $showDocumentPicker, onDismiss: {
            showDocumentPicker = false
        }) {
            DocumentPicker(
                pickedData: $pickedFileData,
                pickedText: $pickedTextContent,
                supportedTypes: [.text, .pdf, .jpeg, .png, .gif, .data]
            )
        }
    }
}

struct DocumentPicker: UIViewControllerRepresentable {
    @Binding var pickedData: Data?
    @Binding var pickedText: String?
    let supportedTypes: [UTType]

    func makeCoordinator() -> Coordinator {
        Coordinator(pickedData: $pickedData, pickedText: $pickedText)
    }

    func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
        let controller = UIDocumentPickerViewController(
            forOpeningContentTypes: supportedTypes,
            asCopy: true
        )
        controller.delegate = context.coordinator
        controller.allowsMultipleSelection = false
        return controller
    }

    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {}
}

class Coordinator: NSObject, UIDocumentPickerDelegate {
    @Binding var pickedData: Data?
    @Binding var pickedText: String?

    init(pickedData: Binding<Data?>, pickedText: Binding<String?>) {
        _pickedData = pickedData
        _pickedText = pickedText
    }

    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        guard let sourceURL = urls.first else { return }

        // 申请安全域访问权限
        let canAccess = sourceURL.startAccessingSecurityScopedResource()
        // 无论流程成功失败,最后自动释放权限
        defer {
            if canAccess {
                sourceURL.stopAccessingSecurityScopedResource()
            }
        }

        do {
            // 主动拷贝文件到自身沙盒Documents目录,持久化存储
            let sandboxDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let targetURL = sandboxDir.appendingPathComponent(sourceURL.lastPathComponent)
            // 重名文件先删除避免拷贝失败,也可以加唯一后缀区分版本
            if FileManager.default.fileExists(atPath: targetURL.path) {
                try FileManager.default.removeItem(at: targetURL)
            }
            try FileManager.default.copyItem(at: sourceURL, to: targetURL)

            // 从沙盒路径读取文件内容,无权限问题
            let fileData = try Data(contentsOf: targetURL)
            pickedData = fileData
            // 仅当文件是UTF8文本时才解析字符串
            if let text = String(data: fileData, encoding: .utf8) {
                pickedText = text
            }
        } catch {
            print("文件导入失败: \(error)")
        }
    }
}

关键注意事项

  • 如果开启多选支持,需要遍历urls数组中所有URL,每个文件单独走安全域权限申请、拷贝流程
  • 如果需要支持选择文件夹,对返回的文件夹URL用FileManager.default.enumerator(at:)遍历内部子文件后逐个拷贝,不能直接读取文件夹URL
  • 必须在didPickDocumentsAt回调的当前执行流内完成权限申请和拷贝操作,不要异步dispatch到其他队列再访问系统返回的原始URL,否则系统会提前回收临时权限触发报错
  • 必须用defer保证stopAccessingSecurityScopedResource被调用,否则会造成内核资源泄漏,导致后续文件访问异常
  • 图片、PDF、二进制文件读取到Data后,再对应转成UIImage、PDFDocument等业务需要的类型,不要强行转UTF8字符串

内容的提问来源于stack exchange,提问作者Ufuk Köşker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 04:27:21