Swift 非破坏性JSONEncoder:持久化保留付费用户设置方案咨询
问题根因
现有逻辑的核心问题是非付费状态下编码配置时跳过付费字段,写入操作会全量覆盖钥匙串中的旧存储数据,直接抹除历史留存的付费配置值。
要实现需求只需遵循一个核心原则:付费配置持久化留存,仅在读取/业务使用环节做权限拦截,绝对不能在非付费状态下用默认值覆盖存储中的付费字段。
具体实现
1. 调整编码逻辑
非付费状态下写入配置时,不能使用内存中付费字段的默认值覆盖存储,要先读取已有存储中的付费字段值保留写入;付费状态下正常写入当前修改的付费字段值:
func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) // 免费字段全状态正常写入 try container.encode(someURL, forKey: .someURL) if SettingsManager.shared.isPremium { // 付费状态:写入当前内存中最新的付费配置值 try container.encode(someFeature, forKey: .someFeature) try container.encode(someSetting, forKey: .someSetting) } else { // 非付费状态:读取钥匙串中已存的历史付费配置写入,避免被默认值覆盖 // 注意:以下KeychainService方法替换为你项目中实际的钥匙串读取实现 guard let storedData = KeychainService.getData(for: "user_settings"), let storedSettings = try? JSONDecoder().decode(Settings.self, from: storedData) else { // 首次安装无历史数据时写入默认值 try container.encode(false, forKey: .someFeature) try container.encode(150, forKey: .someSetting) return } try container.encode(storedSettings.someFeature, forKey: .someFeature) try container.encode(storedSettings.someSetting, forKey: .someSetting) } }
2. 优化解码逻辑
解码环节仅在付费状态下读取存储的付费字段,非付费状态直接返回默认值,保证免费用户无法读到真实付费配置:
init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 免费字段全状态正常解析 self.someURL = try container.decodeIfPresent(URL.self, forKey: .someURL) ?? URL(string: "https://www.google.com") // 付费字段按权限解析 if SettingsManager.shared.isPremium { self.someFeature = try container.decodeIfPresent(Bool.self, forKey: .someFeature) ?? false self.someSetting = try container.decodeIfPresent(Int.self, forKey: .someSetting) ?? 150 } else { // 非付费状态直接返回默认值,不读取存储值 self.someFeature = false self.someSetting = 150 } }
3. 补充状态联动&安全加固
- 订阅状态变更(订阅成功/到期/恢复购买)时,重新从钥匙串解码一次
Settings并更新内存中的全局实例,保证配置和权限状态同步 - 对付费字段的修改加权限拦截,避免非付费状态下业务逻辑误改付费配置:
struct Settings: Codable { // 付费字段禁止直接修改,通过指定方法更新 private(set) var someFeature: Bool = false private(set) var someSetting: Int = 150 var someURL: URL? = URL(string: "https://www.google.com") /// 仅付费状态下可更新付费配置 mutating func updatePremiumConfig(someFeature: Bool? = nil, someSetting: Int? = nil) { guard SettingsManager.shared.isPremium else { return } if let someFeature = someFeature { self.someFeature = someFeature } if let someSetting = someSetting { self.someSetting = someSetting } } } // CodingKeys保持原有定义,修正属性名匹配问题 private enum CodingKeys : String, CodingKey { case someFeature, someSetting, someURL }
覆盖场景验证
- 付费用户修改付费配置后退订:页面显示付费配置默认值,付费功能受限,存储中历史付费配置保留
- 退订用户重新订阅:自动恢复之前修改的付费配置,无需重新设置
- 非付费状态下多次修改免费配置:不会覆盖存储中留存的历史付费配置
- 非付费状态下任何业务逻辑无法读取、修改真实付费配置值
内容的提问来源于stack exchange,提问作者Martin Claesson
相关产品推荐
相关产品推荐

