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

SwiftUI:如何使用安全作用域保存URL书签

安全作用域书签实现方案

一、替换URL存储为书签数据存储

直接把URL存到@ApplicationStorage,App重启后会因为沙箱权限限制无法访问文件。必须将每个文件URL转换为安全作用域书签(Security-Scoped Bookmark),以Data类型存储。

修改你的存储变量:

// 替换原有的fileURL: [URL]
@ApplicationStorage("fileBookmarks") private var fileBookmarks: [Data] = []

二、生成并存储安全书签

在fileImporter的成功回调里,替换原URL存储逻辑,改为生成书签并保存:

.fileImporter(
    isPresented: $openFile,
    allowedContentTypes: [.image, .pdf],
    allowsMultipleSelection: true,
    onCompletion: { result in
        switch result {
        case .success(let files):
            images.removeAll()
            var newBookmarks: [Data] = []
            
            for file in files {
                let gotAccess = file.startAccessingSecurityScopedResource()
                defer {
                    if gotAccess {
                        file.stopAccessingSecurityScopedResource()
                    }
                }
                guard gotAccess else { continue }
                
                // 保留原有图片读取逻辑
                if let imageData = try? Data(contentsOf: file),
                   let image = UIImage(data: imageData) {
                    images.append(image)
                }
                
                // 生成安全书签
                do {
                    let bookmark = try file.bookmarkData(
                        options: .withSecurityScope,
                        includingResourceValuesForKeys: nil,
                        relativeTo: nil
                    )
                    newBookmarks.append(bookmark)
                } catch {
                    print("生成书签失败: \(error)")
                }
            }
            
            // 存储书签数组
            fileBookmarks = newBookmarks
            
        case .failure(let error):
            print("文件导入失败: \(error)")
        }
    }
)

三、从书签恢复URL并获取访问权限

当需要再次访问文件时,从存储的书签数据恢复URL,并重新申请安全作用域权限:

1. 编写URL恢复工具函数

func restoreURL(from bookmarkData: Data) -> URL? {
    var isStale = false
    do {
        let url = try URL(
            resolvingBookmarkData: bookmarkData,
            options: .withSecurityScope,
            relativeTo: nil,
            bookmarkDataIsStale: &isStale
        )
        
        // 书签过期时重新生成并更新存储
        if isStale {
            guard url.startAccessingSecurityScopedResource() else { return nil }
            defer { url.stopAccessingSecurityScopedResource() }
            
            let newBookmark = try url.bookmarkData(
                options: .withSecurityScope,
                includingResourceValuesForKeys: nil,
                relativeTo: nil
            )
            if let index = fileBookmarks.firstIndex(of: bookmarkData) {
                fileBookmarks[index] = newBookmark
            }
        }
        
        return url
    } catch {
        print("恢复URL失败: \(error)")
        return nil
    }
}

2. 使用恢复后的URL访问文件

比如在需要重新加载文件内容时:

func loadFilesFromBookmarks() {
    images.removeAll()
    for bookmarkData in fileBookmarks {
        guard let url = restoreURL(from: bookmarkData) else { continue }
        
        let gotAccess = url.startAccessingSecurityScopedResource()
        defer {
            if gotAccess {
                url.stopAccessingSecurityScopedResource()
            }
        }
        guard gotAccess else { continue }
        
        // 加载图片逻辑
        if let imageData = try? Data(contentsOf: url),
           let image = UIImage(data: imageData) {
            images.append(image)
        }
        // PDF文件可对应处理,比如生成PDF文档实例
    }
}

四、关键注意事项

  • 权限释放:用defer语句确保获取安全作用域权限后,无论代码执行结果如何,都能及时释放权限,避免资源泄漏。
  • 书签过期处理:当书签标记为isStale时,必须重新生成书签并更新存储,否则后续无法访问对应文件。
  • 错误处理:实际开发中不要用try?忽略所有错误,建议针对权限不足、文件被删除等具体错误做用户提示。
  • 沙箱配置:在Xcode的Signing & Capabilities中开启App Sandbox,并勾选「User Selected File」权限,确保App有权限访问用户选择的文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 12:52:25