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

Ran*_*rns 6 struct properties ios swift userdefaults

我的应用程序使用一个可编码的结构,如下所示:

public struct UserProfile: Codable {

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

     var levelScores: [LevelScore] = []
}
Run Code Online (Sandbox Code Playgroud)

AJSONEncoder()已用于将UserProfiles的编码数组保存为用户默认值。

在即将到来的更新中,我想向这个UserProfile结构添加一个新属性。这在某种程度上可能吗?

或者我是否需要创建一个具有相同属性和一个新属性的新 Codable 结构,然后将所有值复制到新结构中,然后开始使用该新结构代替UserProfile之前使用该结构的任何地方?

如果我只是向结构体添加一个新属性,那么我将无法加载之前编码的UserProfiles数组,因为结构体将不再具有匹配的属性。当我使用此代码加载已保存的用户时:

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

loadedUsers如果在UserProfile编码和保存时具有的属性不包含结构的所有当前属性,则不解码UserProfile

有关更新保存的结构属性的任何建议?或者我必须走很长的路并重新创建,因为我之前没有提前计划要包含此属性?

谢谢你的帮助!

Joa*_*son 6

如评论中所述,您可以将新属性设为可选,然后解码将适用于旧数据。

var newProperty: Int?
Run Code Online (Sandbox Code Playgroud)

另一种选择是如果属性丢失,则在解码期间应用默认值。

这可以通过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)
}
Run Code Online (Sandbox Code Playgroud)

这需要您定义一个CodingKey枚举

enum CodingKeys: String, CodingKey {
    case name, completedGame, newProperty, levelScores
}
Run Code Online (Sandbox Code Playgroud)

如果在代码中使用时不希望它是可选的,则第三个选项是将其包装在计算的非可选属性中,再次使用默认值。这里_newProperty将在存储的jsonnewProperty中使用但在代码中使用

private var _newProperty: Int?
var newProperty: Int {
    get {
        _newProperty ?? -1
    }
    set {
        _newProperty = newValue
    }
}
Run Code Online (Sandbox Code Playgroud)