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

SwiftUI中如何使用fileExporter导出包文档?

用fileExporter导出包文档的解决方案

要导出包含多文件的包文档,核心是通过FileDocument协议的fileWrapper方法处理目录结构,而非单文件的Data。以下是具体实现步骤:

1. 注册包类型的自定义UTType

确保自定义UTType被标记为包类型,让系统识别其为目录结构的包:

import UniformTypeIdentifiers

extension UTType {
    static let myAppPackage = UTType(exportedAs: "com.yourcompany.MyAppFile")
    
    static func registerMyAppPackage() {
        let utType = UTType(
            exportedAs: "com.yourcompany.MyAppFile",
            conformingTo: .package, // 关键:指定为包类型
            description: "My App Package",
            tags: [.filenameExtension: "myfile"]
        )
        UTType.register(utType)
    }
}

在App的初始化方法中调用UTType.registerMyAppPackage()完成注册,同时在项目Info.plist中配置UTType信息(设置CFBundleTypeRole为Editor,LSItemContentTypes包含你的UTType标识)。

2. 实现包类型的FileDocument

定义遵循FileDocument的结构体,重写fileWrapper(configuration:)方法构建包的目录结构:

import SwiftUI
import UniformTypeIdentifiers

struct MyAppPackageDocument: FileDocument {
    static var readableContentTypes: [UTType] { [.myAppPackage] }
    static var writableContentTypes: [UTType] { [.myAppPackage] }
    
    // 存储包内所有文件数据
    var projectJSONData: Data
    var mediaFiles: [String: Data] // key为文件名,value为文件数据
    
    init(projectJSONData: Data, mediaFiles: [String: Data]) {
        self.projectJSONData = projectJSONData
        self.mediaFiles = mediaFiles
    }
    
    // 从现有包加载数据(可选,需打开功能时实现)
    init(configuration: ReadConfiguration) throws {
        guard let rootWrapper = configuration.fileWrappers?["."] else {
            throw CocoaError(.fileReadCorruptFile)
        }
        
        // 读取project.json
        guard let jsonWrapper = rootWrapper.fileWrappers?["project.json"],
              let jsonData = jsonWrapper.regularFileContents else {
            throw CocoaError(.fileReadCorruptFile)
        }
        projectJSONData = jsonData
        
        // 读取所有媒体文件
        var media = [String: Data]()
        for (filename, wrapper) in rootWrapper.fileWrappers ?? [:] {
            guard filename != "project.json", let data = wrapper.regularFileContents else {
                continue
            }
            media[filename] = data
        }
        mediaFiles = media
    }
    
    // 生成包的FileWrapper用于导出
    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let rootWrapper = FileWrapper(directoryWithFileWrappers: [:])
        
        // 添加project.json到包中
        let jsonWrapper = FileWrapper(regularFileWithContents: projectJSONData)
        rootWrapper.addFileWrapper(jsonWrapper, withFilename: "project.json")
        
        // 添加所有媒体文件到包中
        for (filename, data) in mediaFiles {
            let mediaWrapper = FileWrapper(regularFileWithContents: data)
            rootWrapper.addFileWrapper(mediaWrapper, withFilename: filename)
        }
        
        return rootWrapper
    }
}

3. 使用fileExporter导出包文档

在View中通过fileExporter修饰符触发导出流程,传入包文档实例:

struct ContentView: View {
    @State private var isExporting = false
    @State private var packageDocument: MyAppPackageDocument?
    
    var body: some View {
        Button("导出包文档") {
            // 准备project.json数据(替换为你的业务数据)
            let projectDict = ["projectName": "My Project", "createdAt": Date().description]
            guard let jsonData = try? JSONSerialization.data(withJSONObject: projectDict) else {
                return
            }
            
            // 准备媒体文件数据(实际中从内存或本地文件读取)
            let mediaFiles = [
                "file001.mp4": Data(), // 替换为实际MP4数据
                "file002.jpg": Data(), // 替换为实际JPG数据
                "file003.mp3": Data()  // 替换为实际MP3数据
            ]
            
            // 创建包文档实例
            packageDocument = MyAppPackageDocument(projectJSONData: jsonData, mediaFiles: mediaFiles)
            isExporting = true
        }
        .fileExporter(
            isPresented: $isExporting,
            document: packageDocument,
            contentType: .myAppPackage,
            defaultFilename: "MyAppFile"
        ) { result in
            switch result {
            case .success(let url):
                print("包文档导出成功:\(url.path)")
            case .failure(let error):
                print("导出失败:\(error.localizedDescription)")
            }
        }
    }
}

关键说明

  • 系统会自动将FileWrapper目录结构打包为一个包文件(macOS显示为单个文件,iOS作为整体保存)
  • 确保所有要导出的文件数据已加载到内存,fileWrapper方法会将它们组合成目录结构后导出
  • 处理大文件时,建议先将临时文件写入磁盘,再通过FileWrapper(url:)添加到包中,避免内存占用过高

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 05:49:53