【问题标题】:Swift: Add property to a struct that gets encoded to JSONSwift:将属性添加到编码为 JSON 的结构中
【发布时间】:2021-01-09 09:13:54
【问题描述】:

我有一个用于用户偏好的 Struct,这个对象被编码并存储为 JSON 文件并存储用于 iCloud Document Sync(我发现它比 NSUbiquitousContainer 更可靠,即使这是你应该使用的偏好)。

假设在应用程序的 v1 中,结构看起来像这样,所有用户都有一个包含这 3 个属性的 JSON 文件。

struct Preferences: Codable {
    var soundsDisabled: Bool = false
    var hapticsDisabled: Bool = false
    var badgeNumberEnabled: Bool = false
}

我需要弄清楚的是,是否有一种方法可以将 v1.1 中的第 4 个属性添加到此 Struct 而不会弄乱现有的 JSON。现在,如果我添加一个新属性,所有内容都会重置为默认值,并且用户的偏好会丢失。

更新:这就是这个结构被编码为 JSON 的方式

extension Storage {
    
    static var preferences: Preferences {
        get {
            guard
                let data = try? Data(contentsOf: Storage.preferencesFile),
                let decoded = try? JSONDecoder().decode(Preferences.self, from: data)
            else { return Preferences() }
            return decoded
        }
        set {
            do { try JSONEncoder().encode(newValue).write(to: Storage.preferencesFile, options: .atomic) }
            catch let error {
                print("Failed to write Preferences: \(error.localizedDescription)")
                MSAnalytics.trackEvent("Failed to write preferences", withProperties: ["Error": error.localizedDescription])
            }
        }
    }

}

谢谢。

【问题讨论】:

  • 你能展示你如何保存和检索保存的数据吗? “一切都被重置为默认值,用户的偏好会丢失。”不是默认情况下会发生的事情。
  • 感谢@Sweeper,刚刚意识到并添加了代码。
  • 文件解码失败,因为模型改变了,一个新的对象被那个 else 子句初始化了。那么有没有办法用新模型成功解码旧文件?还是我想错了。

标签: json swift struct


【解决方案1】:

这可能是因为JSONDecoder 无法再将存储的数据解码为Preferences 实例。 (在这种情况下,您将返回一个新实例 Preferences())

您可以做的是将您的新属性添加为可选属性,如下所示:

struct Preferences: Codable {
    var soundsDisabled: Bool = false
    var hapticsDisabled: Bool = false
    var badgeNumberEnabled: Bool = false
    var newProperty: Bool? = false
}

那么在解码旧数据后,newProperty 将是nil。

更新:另一个可能更好的解决方案是自己实现解码,当没有属性newProperty时设置默认值:

struct Preferences: Codable {
    var soundsDisabled: Bool = false
    var hapticsDisabled: Bool = false
    var badgeNumberEnabled: Bool = false
    var newProperty: Bool = false
    
    enum CodingKeys: String, CodingKey {
        case soundsDisabled
        case hapticsDisabled
        case badgeNumberEnabled
        case newProperty
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        soundsDisabled = try container.decode(Bool.self, forKey: .soundsDisabled)
        hapticsDisabled = try container.decode(Bool.self, forKey: .hapticsDisabled)
        badgeNumberEnabled = try container.decode(Bool.self, forKey: .badgeNumberEnabled)

        // Decode newProperty and if not present set default value
        newProperty = try container.decodeIfPresent(Bool.self, forKey: .newProperty) ?? false
    }
}

【讨论】:

  • 太棒了,这很有意义。谢谢你的回答。
猜你喜欢
  • 2015-06-13
  • 1970-01-01
  • 1970-01-01
  • 2021-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-12
相关资源
最近更新 更多