iOS真机无法写入文件但模拟器正常,求技术解决方案
解决iOS真机上通过UIDocumentPicker写入外部文件夹失败的问题
核心问题分析
你遇到的问题根源在于:对通过UIDocumentPickerViewController获取的文件夹的子文件,不需要单独调用startAccessingSecurityScopedResource()。这个方法仅适用于直接从文档选择器获取的根资源(即选中的文件夹URL),真机系统不会为尚未创建的文件分配安全范围权限,因此调用会失败。而模拟器的权限校验更宽松,所以不会触发这个问题。
修正后的代码
移除对fileUrl的安全范围资源调用,仅保留父文件夹的权限处理即可:
guard folderUrl.startAccessingSecurityScopedResource() else { print("Failed to obtain access to folder \(folderUrl)") return } defer { folderUrl.stopAccessingSecurityScopedResource() } let fileUrl = folderUrl.appendingPathComponent("HelloWorld.txt") let fileData = "Hello World".data(using: .utf8) do { try fileData?.write(to: fileUrl, options: .atomic) print("File written successfully to \(fileUrl)") } catch { print("Failed to write file: \(error.localizedDescription)") }
关键注意事项
- 不要对子资源调用安全范围方法:父文件夹的安全范围权限会自动覆盖其下的所有子文件和子文件夹,无论是已存在还是新创建的。
- 持久化访问权限:如果需要在APP重启后仍能访问该文件夹,需要将获取到的
folderUrl转换为bookmark并存储(比如存入UserDefaults),下次启动时再还原为URL并重新获取权限。示例代码如下:// 存储bookmark if let bookmarkData = try? folderUrl.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) { UserDefaults.standard.set(bookmarkData, forKey: "SelectedFolderBookmark") } // 还原URL if let bookmarkData = UserDefaults.standard.data(forKey: "SelectedFolderBookmark") { var isStale = false if let restoredUrl = try? URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale) { if isStale { // 需要重新获取bookmark } if restoredUrl.startAccessingSecurityScopedResource() { defer { restoredUrl.stopAccessingSecurityScopedResource() } // 使用restoredUrl进行操作 } } } - 避免使用
try!:实际开发中一定要用do-catch捕获写入错误,这样能排查出诸如权限不足、磁盘空间不够等问题。
内容的提问来源于stack exchange,提问作者Deling Ren
相关产品推荐
相关产品推荐

