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

