Swift中JSON播放列表保存后加载提示文件不存在的问题排查
播放列表JSON文件保存后加载提示不存在的问题排查
我写了一个将播放列表保存到本地JSON文件的函数,但后续解析该文件时,Xcode提示指定路径下的文件不存在,可文件实际确实存在。
相关代码
func savePlaylistData(lista: PlayListModel, _ playlistData: [PlaylistGroup: [PlaylistItem]], to filename: String) async throws { // 创建临时字典,用字符串作为键 var tempDict: [String: [PlaylistItem]] = [:] // 将PlaylistGroup键转换为字符串键 for (key, value) in playlistData { tempDict[key.groupName] = value } // 编码字典为JSON let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted // 让JSON更易读 encoder.dateEncodingStrategy = .iso8601 // 确保日期正确编码 // 在后台线程执行JSON编码和文件写入 try await withCheckedThrowingContinuation { continuation in do { let jsonData = try encoder.encode(tempDict) // 将JSON数据写入带json后缀的文件 let fileURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename).appendingPathExtension("json") try jsonData.write(to: fileURL) print("数据已保存到 \(fileURL.path)") continuation.resume() // 更新PlayListModel lista.haveFile = true lista.playlistUrl = fileURL.path print("已更新PlayListModel,路径为 \(fileURL.path)") // 假设modelContext已正确配置 do { self.modelContext.insert(lista) try modelContext.save() } catch { print("保存PlayListModel失败: \(error)") } } catch { continuation.resume(throwing: error) } } } func loadPlaylistData(from url: URL) async throws -> [PlaylistGroup: [PlaylistItem]] { // 调试:打印URL // 调试:检查文件是否存在 if FileManager.default.fileExists(atPath: url.path) { print("文件存在于 \(url.path)") } else { print("文件不存在于 \(url.path)") throw NSError(domain: "FileError", code: 404, userInfo: [NSLocalizedDescriptionKey: "文件不存在于 \(url.path)"]) } // 调试:检查文件可读性 do { let fileAttributes = try FileManager.default.attributesOfItem(atPath: url.path) print("文件属性: \(fileAttributes)") } catch { print("获取文件属性失败: \(error.localizedDescription)") throw NSError(domain: "FileError", code: 403, userInfo: [NSLocalizedDescriptionKey: "无法读取 \(url.path) 的文件属性"]) } do { // 从文件读取JSON数据 print("正在从 \(url) 读取JSON数据") let jsonData = try Data(contentsOf: url) // 将JSON数据解码为带字符串键的临时字典 let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 // 确保日期正确解码 let tempDict: [String: [PlaylistItem]] do { tempDict = try decoder.decode([String: [PlaylistItem]].self, from: jsonData) } catch let decodeError { print("解码JSON数据失败: \(decodeError.localizedDescription)") throw decodeError } // 将临时字典转换回带PlaylistGroup键的原始格式 var playlistData: [PlaylistGroup: [PlaylistItem]] = [:] for (key, value) in tempDict { let group = PlaylistGroup(groupName: key) playlistData[group] = value } return playlistData } catch let dataError { print("加载并解码文件失败: \(dataError.localizedDescription)") throw dataError } }
问题原因分析
- 临时目录的自动清理机制:iOS的临时目录(
FileManager.default.temporaryDirectory)会被系统自动清理——当APP退到后台、系统磁盘空间不足时,文件可能被删除。你看到的"实际存在"可能是保存后短时间内的状态,加载时文件已被系统清理。 - 路径存储的不准确性:你把
fileURL.path(字符串格式的路径)存在lista.playlistUrl中,APP重启后临时目录的路径可能发生变化,旧的字符串路径会指向错误位置;另外,路径中的特殊字符可能导致字符串转URL时出现编码问题,生成无效的URL。 - Core Data操作的线程不安全:在
savePlaylistData中,你在后台线程的闭包里直接更新modelContext(Core Data上下文),而Core Data上下文默认是线程绑定的,非主线程操作可能导致数据未及时同步,加载时拿到的路径不是最新的。
解决办法
更换持久化存储目录
放弃临时目录,改用文档目录(不会被系统自动清理),修改保存时的URL生成代码:guard let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { throw NSError(domain: "FileError", code: 500, userInfo: [NSLocalizedDescriptionKey: "无法获取文档目录"]) } let fileURL = docDir.appendingPathComponent(filename).appendingPathExtension("json")存储URL而非字符串路径
将PlayListModel中的playlistUrl类型改为URL(Core Data中对应Transformable类型),避免字符串转URL的编码问题,确保路径准确性。保证Core Data操作的线程安全
更新PlayListModel和保存modelContext的操作放到主线程执行(如果modelContext是主线程上下文):DispatchQueue.main.async { [weak self] in guard let self = self else { return } lista.haveFile = true lista.playlistUrl = fileURL // 若改为URL类型 do { self.modelContext.insert(lista) try self.modelContext.save() } catch { print("保存PlayListModel失败: \(error)") } }
内容的提问来源于stack exchange,提问作者Damiano Miazzi
相关产品推荐
相关产品推荐

