更新已保存的 Codable 结构以添加新的 属性
Updating a saved Codable struct to add a new property
我的应用程序使用像这样的可编码结构:
public struct UserProfile: Codable {
var name: String = ""
var completedGame: Bool = false
var levelScores: [LevelScore] = []
}
A 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) {
如果 UserProfile
在编码和保存时具有的属性不包含 UserProfile
结构的所有当前属性,loadedUsers
不会解码。
对更新保存的结构属性有什么建议吗?还是我必须走很远的路并重新创建,因为我之前没有提前计划将此 属性 包含在内?
感谢您的帮助!
如评论中所述,您可以将新的 属性 设为可选,然后解码将适用于旧数据。
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
}
}
我的应用程序使用像这样的可编码结构:
public struct UserProfile: Codable {
var name: String = ""
var completedGame: Bool = false
var levelScores: [LevelScore] = []
}
A 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) {
如果 UserProfile
在编码和保存时具有的属性不包含 UserProfile
结构的所有当前属性,loadedUsers
不会解码。
对更新保存的结构属性有什么建议吗?还是我必须走很远的路并重新创建,因为我之前没有提前计划将此 属性 包含在内?
感谢您的帮助!
如评论中所述,您可以将新的 属性 设为可选,然后解码将适用于旧数据。
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
}
}