SwiftUI封装UIDocumentPickerViewController真机报Code=257文件权限错误
问题原因
iOS系统对外部文件访问有严格的沙盒权限校验,UIDocumentPickerViewController返回的文件URL属于安全作用域资源,访问前必须显式申请临时权限。模拟器的权限校验逻辑做了放宽,所以直接访问不会触发错误,而真机严格执行权限校验,你在没有申请权限的前提下直接读取文件属性,就会抛出Code=257的权限错误。你之前尝试的FileManager读大小、复制文件的操作,本质都是访问外部文件URL,都需要先拿到权限,所以都会触发相同报错。
修复方案
你只需要在访问返回的URL前添加权限申请逻辑,操作完成后释放权限即可:
- 调用
url.startAccessingSecurityScopedResource()申请临时访问权限,仅当返回true时表示权限申请成功 - 所有URL访问逻辑执行完成后,必须配对调用
url.stopAccessingSecurityScopedResource()释放资源 - 如果后续需要长期使用选中的文件,建议在拿到权限后将文件复制到App自身沙盒目录(如Documents、tmp目录),后续访问沙盒内的本地URL不需要再申请权限
修正后的核心代码
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { guard let url = urls.first else { return } // 申请临时访问权限 let hasAccess = url.startAccessingSecurityScopedResource() // 用defer保证不管逻辑走哪个分支都会释放权限 defer { if hasAccess { url.stopAccessingSecurityScopedResource() } } guard hasAccess else { // 权限申请失败的兜底逻辑 print("无法获取文件访问权限") return } do { let resources = try url.resourceValues(forKeys:[.fileSizeKey]) guard let fileSize = resources.fileSize else { return } // 检查文件大小 if Double(fileSize).convertBytesToMegabytes() < 10 { // 如果需要后续使用文件,建议在这里把文件复制到沙盒 let tempDir = FileManager.default.temporaryDirectory let targetURL = tempDir.appendingPathComponent(url.lastPathComponent) if FileManager.default.fileExists(atPath: targetURL.path) { try FileManager.default.removeItem(at: targetURL) } try FileManager.default.copyItem(at: url, to: targetURL) // 把沙盒内的URL回传,后续不需要再申请权限 self.parent.didPickFileURL(targetURL) } else { self.parent.didPickFileOverFileSizeLimit() } } catch { print("文件处理错误: \(error)") } }
内容的提问来源于stack exchange,提问作者Tudor Andreescu
相关产品推荐
相关产品推荐

