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

SwiftUI中fileImporter/fileExporter权限不显示及报错问题

问题1:控制台抛出_UIViewServiceErrorDomain Code=1错误

原因分析

这个错误主要由两个问题触发:

  • fileImporter/fileExporter修饰符被附加到了可能频繁重建的视图(比如你的HStack按钮容器),导致视图服务连接异常断开。
  • fileExporter的完成回调中错误调用了accessingRead(from:)方法——导出操作不需要读取文件,多余的安全域资源访问操作会引发错误。

解决步骤

  1. 将fileImporter和fileExporter修饰符从HStack移到顶层VStack上,避免视图重建导致的服务断开。
  2. 删除fileExporter回调中多余的accessingRead调用,导出完成后仅重置状态即可。
问题2:未弹出文件访问权限请求对话框

原因分析

权限请求不弹出通常是因为Info.plist缺少正确的iCloud容器配置(涉及iCloud Drive访问时),或者配置格式错误。另外,iOS系统仅在首次访问需要权限的存储位置时弹出请求,若之前已拒绝,需手动去系统设置开启权限。

解决步骤

  1. 在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")
  2. 开启项目的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:15:56