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

SwiftUI导出报错:UIDocumentPickerViewController初始化仅支持Import/Open模式

解决方案

一、修复UIDocumentPickerViewController初始化错误

报错原因是UIDocumentPickerViewController(documentTypes:in:)仅支持.import和.open模式,你使用的.exportToService模式不兼容该初始化方法。要实现选择保存位置,需改用.open模式并开启目录选择权限:

修改DocumentPickerView的makeUIViewController方法:

func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentPickerView>) -> UIDocumentPickerViewController {
    let documentTypes = [kUTTypeFolder as String, kUTTypeZipArchive as String]
    let exportPicker = UIDocumentPickerViewController(documentTypes: documentTypes, in: .open)
    exportPicker.delegate = context.coordinator
    exportPicker.modalPresentationStyle = .formSheet
    // 允许用户选择保存目录
    exportPicker.allowsDirectorySelection = true
    // iOS14+优化设置
    if #available(iOS 14.0, *) {
        exportPicker.allowsMultipleSelection = false
        exportPicker.shouldShowFileExtensions = true
    }
    return exportPicker
}

同时调整Coordinator的回调逻辑,在用户选择的目录下生成指定名称的zip文件:

func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    guard let selectedDirURL = urls.first else {
        print("No directory selected for export")
        return
    }
    // 生成目标zip文件路径
    let zipFileName = "exported_data.zip"
    let exportURL = selectedDirURL.appendingPathComponent(zipFileName)
    // 处理已存在的同名文件(示例直接删除,可改为提示用户)
    let fileManager = FileManager.default
    if fileManager.fileExists(atPath: exportURL.path) {
        try? fileManager.removeItem(at: exportURL)
    }
    completion(exportURL)
}

二、修复导出函数逻辑错误

原导出函数存在逻辑冲突:先创建zip文件,再写入图片到同一目录,最后重新压缩会覆盖原有zip,导致最终文件仅包含单张图片。正确流程是先将所有待压缩文件存入临时目录,再一次性压缩:

修改后的exportDataAsZip方法:

func exportDataAsZip(to url: URL) {
    let fileManager = FileManager.default
    // 创建唯一临时目录存放待压缩文件
    let tempDirURL = fileManager.temporaryDirectory.appendingPathComponent("ExportTemp_\(UUID().uuidString)")
    
    do {
        // 创建临时目录
        try fileManager.createDirectory(at: tempDirURL, withIntermediateDirectories: true)
        
        // 1. 复制文档目录内容到临时目录
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        try fileManager.copyItem(at: documentsDirectory, to: tempDirURL.appendingPathComponent("Documents"))
        
        // 2. 导出Core Data图片到临时目录的Images子文件夹
        let imagesDirURL = tempDirURL.appendingPathComponent("Images")
        try fileManager.createDirectory(at: imagesDirURL, withIntermediateDirectories: true)
        
        let managedObjectContext = PersistenceController.shared.container.viewContext
        let fetchRequest: NSFetchRequest<CapturedImage> = CapturedImage.fetchRequest()
        let images = try managedObjectContext.fetch(fetchRequest)
        
        for image in images {
            guard let imageData = image.image, let timestamp = image.timestamp else { continue }
            let dateFormatter = ISO8601DateFormatter()
            let dateString = dateFormatter.string(from: timestamp)
            let imageUrl = imagesDirURL.appendingPathComponent("\(dateString).jpg")
            try imageData.write(to: imageUrl)
        }
        
        // 3. 压缩临时目录到目标zip文件
        let success = SSZipArchive.createZipFile(atPath: url.path, withContentsOfDirectory: tempDirURL.path)
        success ? print("Exported data to: \(url.path)") : print("Failed to create ZIP archive")
        
        // 4. 清理临时目录
        try fileManager.removeItem(at: tempDirURL)
    } catch {
        print("Export error: \(error.localizedDescription)")
        // 异常时清理临时目录
        try? fileManager.removeItem(at: tempDirURL)
    }
}

三、额外配置

  1. 导入必要框架:
import MobileCoreServices
import SSZipArchive
  1. 在Info.plist中添加文件访问权限描述:
<key>NSFileProviderDomainUsageDescription</key>
<string>需要访问Files应用以保存导出的zip文件</string>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:17:36