更新已保存的Codable结构以添加新属性

8

我的应用程序使用一个类似于 Codable 的结构体:

public struct UserProfile: Codable {

     var name: String = ""
     var completedGame: Bool = false

     var levelScores: [LevelScore] = []
}

使用JSONEncoder()将编码后的UserProfile数组保存到用户默认设置中。

在即将发布的更新中,我想向这个UserProfile结构体添加一个新属性。有没有某种可能性?

还是说我需要创建一个新的Codable结构体,它具有相同的属性加上一个新属性,然后将所有值复制到新结构体中,并开始在之前使用UserProfile结构体的任何地方使用这个新结构体?

如果我只是简单地向结构体添加一个新属性,那么我将无法加载先前编码的UserProfile数组,因为该结构体将不再具有匹配的属性。当我到达用于加载保存的用户代码段时:

if let savedUsers = UserDefaults.standard.object(forKey: "SavedUsers") as? Data {
       let decoder = JSONDecoder()
       if let loadedUsers = try? decoder.decode([UserProfile].self, from: savedUsers) {

loadedUsers无法解码,如果UserProfile在编码和保存时拥有的属性不包含当前UserProfile结构体的所有属性。

有没有更新保存的结构体属性的建议?或者我必须绕开这个问题,重新创建一个新的结构体,因为我之前没有考虑到此属性需要包含在内吗?

感谢任何帮助!


2
只要属性是可选的,你就应该能够添加它。 - Paulw11
1个回答

9

正如评论中所提到的,您可以将新属性设置为可选的,这样解码就可以适用于旧数据。

var newProperty: Int?

另一种选择是在解码过程中,如果属性不存在,则应用默认值。

您可以通过在结构体中实现init(from:)来完成此操作。

public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decode(String.self, forKey: .name)
    completedGame = try container.decode(Bool.self, forKey: .completedGame)
    do {
        newProperty = try container.decode(Int.self, forKey: .newProperty)
    } catch {
        newProperty = -1
    }
    levelScores = try container.decode([LevelScore].self, forKey: .levelScores)
}

这需要你定义一个名为 CodingKey 的枚举。
enum CodingKeys: String, CodingKey {
    case name, completedGame, newProperty, levelScores
}

如果你不想在代码中将其设置为可选项,第三个选择是将它包装在计算的非可选属性中,再次使用默认值。这里_newProperty将被用于存储的json中,但newProperty将用于代码中。
private var _newProperty: Int?
var newProperty: Int {
    get {
        _newProperty ?? -1
    }
    set {
        _newProperty = newValue
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接