Swift 4.2:UserDefaults嵌套字典存储及重复检查与更新问题
解决UserDefaults共享嵌套字典的类型转换问题及更新逻辑实现
首先,你存储字典的方式整体是正确的——使用带App Group suiteName的UserDefaults是跨主App和扩展共享数据的标准做法。但问题出在读取时的类型转换:UserDefaults取出的内容默认是Any?类型,你直接强制解包后访问["Title"],其实此时的元素并没有被正确转换成[String: Any]字典,自然返回nil,遍历也会触发类型不匹配的错误。
一、正确的读取方式
先一步步安全地做类型转换,避免强制解包的风险:
// 获取group的UserDefaults实例,建议用可选绑定避免崩溃 guard let userDefaults = UserDefaults(suiteName: "group.identifier.test") else { print("无法访问共享UserDefaults") return } // 取出保存的字典,并将"Sections"对应的内容转成[[String: Any]]类型 guard let savedDict = userDefaults.dictionary(forKey: "savedDict"), var sections = savedDict["Sections"] as? [[String: Any]] else { print("读取Sections失败或格式错误") return }
到这里,sections就是正确类型的数组,每个元素都是可以访问"Title"和"Items"的字典了。
二、实现检查Title存在则删除并添加新Items的逻辑
假设你要操作的目标Title是targetTitle,新的Items数组是newItems,可以这样做:
let targetTitle = "Title1" let newItems = ["item1-new-1", "item1-new-2"] // 1. 找到目标Title对应的索引并删除 sections.removeAll { section in guard let title = section["Title"] as? String else { return false } return title == targetTitle } // 2. 添加新的条目(原Title + 新Items) let newSection = ["Title": targetTitle, "Items": newItems] sections.append(newSection) // 3. 将更新后的sections重新存回UserDefaults userDefaults.set(["Sections": sections], forKey: "savedDict") // 确保同步(虽然通常自动同步,但跨扩展时建议手动调用) userDefaults.synchronize()
三、验证读取结果
现在再读取验证,就能正确获取Title了:
// 重新读取验证 if let updatedSections = userDefaults.dictionary(forKey: "savedDict")?["Sections"] as? [[String: Any]] { for section in updatedSections { if let title = section["Title"] as? String, let items = section["Items"] as? [String] { print("Title: \(title), Items: \(items)") } } }
关键注意点
- 避免强制解包:用
if let/guard let做类型检查和转换,防止因数据格式异常导致崩溃; - 使用
dictionary(forKey:)替代value(forKey:):前者直接返回[String: Any]?,比value(forKey:)返回的Any?更贴合你的数据类型,减少转换步骤; - 跨扩展同步:修改数据后调用
synchronize(),确保主App和扩展能及时拿到最新数据; - 数据类型一致性:存储和读取时要保证所有嵌套元素的类型统一(比如
Items始终是[String],Title始终是String),否则类型转换会失败。
内容的提问来源于stack exchange,提问作者HKEF
相关产品推荐
相关产品推荐

