Swift中为本地JSON添加checked字段留存勾选状态的技术问询
解决方案:为API数据添加勾选状态并持久化保留
针对你的问题,我分两部分来解决:一是给从API解析的Items添加勾选状态字段,二是在每周更新JSON后保留用户的勾选状态。
1. 为Items添加checked字段(兼容API解析)
你不需要修改API返回的JSON,只需要调整本地的Codable结构体,新增checked属性并让解析器忽略它即可。同时建议遵循Swift的命名规范(小驼峰命名):
struct Base : Codable { let expireDate: String let week: [Week] // 原"Week"映射为小写week enum CodingKeys: String, CodingKey { case expireDate case week = "Week" } } struct Week : Codable { let name: String let items: [Item] // 原"Items"改为Item,更符合单例命名 } struct Item : Codable { let icon: String let text: String var checked: Bool = false // 新增勾选状态,默认未勾选 enum CodingKeys: String, CodingKey { case icon = "Icon" // 映射API的"Icon"字段 case text // 不包含checked,解析API JSON时会自动忽略这个字段 } }
这样修改后,解析API返回的JSON时不会报错,因为CodingKeys里没有包含checked,而checked有默认值false,完美兼容原有逻辑。
2. 持久化保留勾选状态(避免被新JSON覆盖)
因为每周会下载新的JSON并覆盖本地存储,所以不能把checked状态存在同一个JSON文件里。我们需要单独用UserDefaults存储每个Item的勾选状态,用Week名称 + Item文本作为唯一标识(如果你的Item有唯一ID,用ID会更可靠,这里假设text是唯一的)。
步骤1:添加状态存储/读取工具方法
// 存储单个Item的勾选状态 private func saveItemCheckedState(weekName: String, itemText: String, isChecked: Bool) { let defaults = UserDefaults.standard let uniqueKey = "\(weekName)-\(itemText)" defaults.set(isChecked, forKey: uniqueKey) } // 读取单个Item的勾选状态(默认返回false) private func getItemCheckedState(weekName: String, itemText: String) -> Bool { let defaults = UserDefaults.standard let uniqueKey = "\(weekName)-\(itemText)" return defaults.bool(forKey: uniqueKey) }
步骤2:加载数据时恢复勾选状态
不管是从API下载新数据,还是从本地读取旧数据,都需要遍历所有Item,恢复之前存储的勾选状态:
private func restoreCheckedStates(for base: Base) -> Base { var mutableBase = base // 遍历每个Week for weekIndex in mutableBase.week.indices { var mutableWeek = mutableBase.week[weekIndex] // 遍历Week下的每个Item for itemIndex in mutableWeek.items.indices { let item = mutableWeek.items[itemIndex] let isChecked = getItemCheckedState(weekName: mutableWeek.name, itemText: item.text) mutableWeek.items[itemIndex].checked = isChecked } mutableBase.week[weekIndex] = mutableWeek } return mutableBase }
然后在你的数据加载逻辑中调用这个方法:
func loadJson() { getItems(for: 1) { [weak self] result in guard let self = self else { return } switch result { case .success(let rawItem): // 恢复勾选状态 let itemWithCheckedStates = self.restoreCheckedStates(for: rawItem) self.saveItemsToDisk(items: itemWithCheckedStates) self.defaults.set(rawItem.expireDate, forKey: "LastUpdateItems") self.itemsDisk = itemWithCheckedStates case .failure(let error): fatalError("error: \(error.localizedDescription)") } // 本地加载后也需要恢复状态 if var localItems = self.itemsDisk { localItems = self.restoreCheckedStates(for: localItems) self.itemsDisk = localItems } } }
步骤3:用户勾选/取消时更新存储
在子TableView的点击事件中,切换Item的checked状态并同步存储:
// 假设这是子TableView的didSelectRowAt方法 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let currentWeek = currentWeek, var items = currentWeek.items else { return } // 切换勾选状态 items[indexPath.row].checked.toggle() // 更新数据源 currentWeek.items = items // 存储新状态到UserDefaults let item = items[indexPath.row] saveItemCheckedState(weekName: currentWeek.name, itemText: item.text, isChecked: item.checked) // 刷新当前cell tableView.reloadRows(at: [indexPath], with: .automatic) }
为什么这样做?
- 单独存储勾选状态到
UserDefaults,不会被每周更新的JSON覆盖,确保用户的选择不会丢失。 - 新增的Item默认
checked为false,符合用户预期,且不会影响原有已勾选的Item。 - 结构体的修改完全兼容原有的API解析逻辑,不需要改动JSON下载和解析的核心代码。
内容的提问来源于stack exchange,提问作者Nxt97
相关产品推荐
相关产品推荐

