Swift Playgrounds 4.3保存JSON文件遇权限问题,求解决方法
问题背景
在macOS 13系统上使用Swift Playgrounds 4.3保存JSON文件到本地时,遇到权限不足或文件找不到的问题,具体场景如下:
1. 自定义路径写入报错
尝试直接写入/Users/la/JSON目录时触发权限错误:
import Foundation struct Driver: Codable { let name: String let ownsCar: Bool let license_code: String? init(name: String, ownsCar: Bool, license_code: String? = nil) { self.name = name self.ownsCar = ownsCar self.license_code = license_code } } class Drivers { let filePathURL = URL(fileURLWithPath: "/Users/la/JSON/JSONTestFile.json") var data: Driver = Driver(name: "Picard", ownsCar: true) func readFromFile(filePathURL: URL) throws { let readData = try Data(contentsOf: filePathURL) self.data = try JSONDecoder().decode(Driver.self, from: readData) } func writeToFile(filePathURL: URL) throws { let jsonData = try JSONEncoder().encode(self.data) try jsonData.write(to: filePathURL) } } let drivers: Drivers = Drivers() do { try drivers.writeToFile(filePathURL: drivers.filePathURL ) } catch { print("Error saving file at path: \(drivers.filePathURL) with error: \(error)") }
错误信息:
Error saving file at path: file:///Users/la/JSON/JSONTestFile.json
with error: Error Domain=NSCocoaErrorDomain Code=513 "You don’t have
permission to save the file “JSONTestFile.json” in the folder “JSON”."
UserInfo={NSFilePath=/Users/la/JSON/JSONTestFile.json,
NSUnderlyingError=0x7f88474252b0 {Error Domain=NSPOSIXErrorDomain
Code=1 "Operation not permitted"}}
2. 旧版Shared Playground Data路径不可用
尝试使用playgroundSharedDataDirectory时触发API不可用错误:
import Foundation import PlaygroundSupport import Swift struct Driver: Codable { let name: String let ownsCar: Bool let license_code: String? init(name: String, ownsCar: Bool, license_code: String? = nil) { self.name = name self.ownsCar = ownsCar self.license_code = license_code } } class Drivers { let filePathURL = URL(fileURLWithPath: "/Users/la/Documents/Shared Playground Data/JSONTestFile.json") // print (playgroundSharedDataDirectory) // let filePathURL = playgroundSharedDataDirectory.appendingPathComponent("JSONTestFile.json") var data: Driver = Driver(name: "Picard", ownsCar: true) func readFromFile(filePathURL: URL) throws { let readData = try Data(contentsOf: filePathURL) self.data = try JSONDecoder().decode(Driver.self, from: readData) } func writeToFile(filePathURL: URL) throws { let jsonData = try JSONEncoder().encode(self.data) try jsonData.write(to: filePathURL) } } let drivers: Drivers = Drivers() do { try drivers.writeToFile(filePathURL: drivers.filePathURL ) } catch { print("Error saving file at path: \(drivers.filePathURL) with error: \(error)") }
报错内容:'playgroundSharedDataDirectory' is unavailable: playgroundSharedDataDirectory is not supported in Swift Playgrounds
3. DocumentDirectory写入无报错但找不到文件
使用系统DocumentDirectory路径时,代码无报错但无法定位生成的文件:
let fileName = "JSONTestFile.json" let docDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let outputFileUrl = docDirectory.appendingPathComponent(fileName) func documentDirectoryURL() -> URL { let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) return paths[0] } func filePathURL() -> URL { return documentDirectoryURL().appendingPathComponent(fileName) }
解决方案
1. 理解沙箱限制
macOS版Swift Playgrounds运行在沙箱环境中,默认仅能访问自身容器内的文件,无法直接读写系统自定义目录或旧版Shared Playground路径,这是系统安全机制的要求。
2. 写入Playgrounds专属可读写目录
Swift Playgrounds 4.x的可读写目录位于其容器内的Documents文件夹,以下是修改后的完整可运行代码:
import Foundation struct Driver: Codable { let name: String let ownsCar: Bool let license_code: String? init(name: String, ownsCar: Bool, license_code: String? = nil) { self.name = name self.ownsCar = ownsCar self.license_code = license_code } } class Drivers { var data: Driver = Driver(name: "Picard", ownsCar: true) // 获取Playgrounds容器内的Documents路径 private func getDocumentsURL() -> URL { let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "com.apple.swiftplaygrounds")! return containerURL.appendingPathComponent("Documents") } func writeToFile() throws { let filePath = getDocumentsURL().appendingPathComponent("JSONTestFile.json") // 确保目录存在 try FileManager.default.createDirectory(at: getDocumentsURL(), withIntermediateDirectories: true) let jsonData = try JSONEncoder().encode(data) try jsonData.write(to: filePath) print("文件已保存到:\(filePath)") } func readFromFile() throws { let filePath = getDocumentsURL().appendingPathComponent("JSONTestFile.json") let readData = try Data(contentsOf: filePath) data = try JSONDecoder().decode(Driver.self, from: readData) print("读取的数据:\(data)") } } let drivers = Drivers() do { try drivers.writeToFile() // 可选:验证读取功能 try drivers.readFromFile() } catch { print("操作失败:\(error)") }
3. 找到生成的文件
运行代码后,通过以下步骤定位文件:
- 在Swift Playgrounds中打开目标项目
- 点击顶部菜单栏
Playground->Show in Finder - 在Finder中右键点击playground文件,选择
Show Package Contents - 导航到
Contents->Documents,即可找到保存的JSONTestFile.json
4. 手动选择路径(绕过沙箱)
如果需要将文件保存到自定义位置,可使用系统文件选择器让用户主动授权,代码如下:
import Foundation import AppKit func saveFileWithPanel(data: Data, fileName: String) { let savePanel = NSSavePanel() savePanel.title = "保存JSON文件" savePanel.nameFieldStringValue = fileName savePanel.allowedContentTypes = [.json] if savePanel.runModal() == .OK { do { try data.write(to: savePanel.url!) print("文件已保存到:\(savePanel.url!)") } catch { print("保存失败:\(error)") } } } // 使用示例 let driver = Driver(name: "Picard", ownsCar: true) let jsonData = try! JSONEncoder().encode(driver) saveFileWithPanel(data: jsonData, fileName: "JSONTestFile.json")
注意:此方案需要导入AppKit框架,运行时会弹出系统保存对话框,用户选择路径后即可完成保存。
内容的提问来源于stack exchange,提问作者iPadawan

