SwiftUI中fileImporter/fileExporter权限不显示及报错问题
问题1:控制台抛出
_UIViewServiceErrorDomain Code=1错误 原因分析
这个错误主要由两个问题触发:
fileImporter/fileExporter修饰符被附加到了可能频繁重建的视图(比如你的HStack按钮容器),导致视图服务连接异常断开。fileExporter的完成回调中错误调用了accessingRead(from:)方法——导出操作不需要读取文件,多余的安全域资源访问操作会引发错误。
解决步骤
- 将
fileImporter和fileExporter修饰符从HStack移到顶层VStack上,避免视图重建导致的服务断开。 - 删除
fileExporter回调中多余的accessingRead调用,导出完成后仅重置状态即可。
问题2:未弹出文件访问权限请求对话框
原因分析
权限请求不弹出通常是因为Info.plist缺少正确的iCloud容器配置(涉及iCloud Drive访问时),或者配置格式错误。另外,iOS系统仅在首次访问需要权限的存储位置时弹出请求,若之前已拒绝,需手动去系统设置开启权限。
解决步骤
- 在Info.plist中添加iCloud容器配置:
- 右键Info.plist → 选择"Add Row"
- 键名选择
NSUbiquitousContainers(类型为Dictionary) - 在
NSUbiquitousContainers下添加子项,键名为你的iCloud容器ID(格式通常为iCloud.com.yourcompany.yourappname),类型为Dictionary - 在该子项中添加两个键:
NSUbiquitousContainerIsDocumentScopePublic(Boolean类型,值设为YES)NSUbiquitousContainerName(String类型,值设为你的应用名称,比如"My File App")
- 开启项目的iCloud能力:
- 打开Xcode项目 → 选择目标 → 切换到"Signing & Capabilities"标签
- 点击"+ Capability" → 搜索"iCloud"并添加
- 在iCloud配置中勾选"iCloud Documents",确认容器ID与Info.plist中的一致
修改后的完整代码
import SwiftUI import UniformTypeIdentifiers struct ContentView: View { @State private var isImport = false @State private var fileImport: String = "" @State private var isExport = false @State private var fileExport: String = "" var body: some View { VStack { ScrollView(.vertical) { Text("\(fileImport.isEmpty ? "" : "Import File -")\(fileImport)") } ///Buttons HStack { Button { isImport.toggle() } label: { Text("Importer") .padding() .background(Color.orange.gradient) .clipShape(.capsule) } Button { fileExport = makeDocument() if !fileExport.isEmpty { isExport.toggle() } } label: { Text("Exporter") .padding() .background(Color.red.gradient) .clipShape(.capsule) } } } .frame(maxWidth: .infinity, maxHeight: .infinity) .background{ Color.cyan.ignoresSafeArea() } // 将fileImporter移到顶层VStack .fileImporter(isPresented: $isImport, allowedContentTypes: [.json]) { result in let result = result.flatMap { accessingRead(from: $0) } switch result { case .success(let file): self.fileImport = file case .failure(let error): print(error.localizedDescription) } } // 将fileExporter移到顶层VStack,并移除多余的accessingRead调用 .fileExporter(isPresented: $isExport, document: TextDocument(fileExport), contentType: .json, defaultFilename: "MyDocument.json") { result in switch result { case .success(_): fileExport = "" case .failure(let error): print(error.localizedDescription) } } } private func accessingRead(from url: URL) -> Result<String,Error> { let accessing = url.startAccessingSecurityScopedResource() defer { if accessing { url.stopAccessingSecurityScopedResource() } } return Result { try String(contentsOf: url) } } private func makeDocument() -> String { let items: [ExportSomeModel] = [ .init(user: "Max", about: "Hello 1", age: 21), .init(user: "Viki", about: "Hello 2", age: 31), .init(user: "Jon", about: "Hello 3", age: 41), .init(user: "Rick", about: "Hello 4", age: 51) ] do { let data = try JSONEncoder().encode(items) let jsonString = String(data: data, encoding: .utf8) if let jsonString { return jsonString } else { return "" } } catch { print(error.localizedDescription) return "" } } } struct TextDocument: FileDocument { static var readableContentTypes: [UTType] { [.json] } var jsonString: String init(_ jsonString: String) { self.jsonString = jsonString } init(configuration: ReadConfiguration) throws { if let data = configuration.file.regularFileContents { jsonString = String(decoding: data, as: UTF8.self) } else { jsonString = "" } } func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { let data = Data(jsonString.utf8) return FileWrapper(regularFileWithContents: data) } } struct ExportSomeModel: Codable { let user: String let about: String let age: Int } #Preview { ContentView() }
额外注意事项
- 若之前已拒绝权限请求,需前往设置 → 你的应用 → 权限手动开启文件访问权限。
- 模拟器测试iCloud功能需登录Apple ID,否则无法正常触发权限请求。
内容的提问来源于stack exchange,提问作者Alexander Karamanets
相关产品推荐
相关产品推荐

