Swift+Cocoa文件下载器:如何在不清除旧数据的情况下更新Plist下载历史
解决Plist写入覆盖原有下载历史的问题
嘿,这个问题很常见——你现在的代码每次都是直接创建一个全新的downloadList对象写入文件,自然会把旧数据完全覆盖掉。要保留历史数据,正确的思路是先读取已有的下载记录,把新条目添加进去,再整体写入文件。我来帮你一步步修改代码:
第一步:修改Codable结构体的可变性
首先你的downloadList结构体里的root是let常量,没法修改数组内容。得把它改成var变量,允许我们添加新条目:
public struct downloadList: Codable { var root: [downloadListt] // 将let改为var,支持数组修改 } public struct downloadListt: Codable { let downloadURL: String let fileName: String }
第二步:添加读取已有下载历史的方法
写一个专门的方法加载本地已有的Plist数据,同时处理文件不存在(第一次写入场景)或者读取失败的情况:
func loadExistingDownloads() -> downloadList { guard let pListFileURL = uniqueDataDir()?.appendingPathComponent("downloads.plist") else { return downloadList(root: []) } // 如果文件还没创建过,直接返回空的下载列表 guard FileManager.default.fileExists(atPath: pListFileURL.path) else { return downloadList(root: []) } do { let data = try Data(contentsOf: pListFileURL) let decoder = PropertyListDecoder() return try decoder.decode(downloadList.self, from: data) } catch { print("读取下载历史失败:\(error)") // 读取出错时也返回空数组,避免程序崩溃 return downloadList(root: []) } }
第三步:修改写入逻辑,合并新旧数据
把原来的“直接写入新数据”改成“读取旧数据→添加新条目→写回文件”的流程,同时优化可选值处理避免崩溃:
// 先安全处理可选值,不要用强制解包 guard let downloadURL = response.url?.absoluteString, let fileName = response.suggestedFilename else { print("无法获取下载URL或文件名") return } // 1. 创建新的下载条目 let newItem = downloadListt(downloadURL: downloadURL, fileName: fileName) // 2. 加载已有的下载历史 var existingDownloads = loadExistingDownloads() // 3. 将新条目添加到历史数组中 existingDownloads.root.append(newItem) // 4. 编码并写入文件 let encoder = PropertyListEncoder() encoder.outputFormat = .xml guard let pListFileURL = uniqueDataDir()?.appendingPathComponent("downloads.plist") else { return } do { let data = try encoder.encode(existingDownloads) try data.write(to: pListFileURL) print("下载历史更新成功") } catch { print("写入下载历史失败:\(error)") }
额外的优化建议
- 线程安全:如果你的下载操作在后台线程执行,建议把Plist读写放到串行队列里处理,避免并发读写导致的数据损坏。
- 数据去重:如果不想重复存储同一个下载条目,可以在添加新条目前检查
existingDownloads.root里是否已有相同downloadURL的记录,避免冗余数据。 - 目录创建:确保
uniqueDataDir()返回的目录已经存在,可以用FileManager.default.createDirectory(at:withIntermediateDirectories:attributes:)提前创建目录。
内容的提问来源于stack exchange,提问作者Donald Timberlake
相关产品推荐
相关产品推荐

