You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 18:48:41