Обновление сохраненной структуры Codable для добавления нового свойства

В моем приложении используется кодируемая структура, например:

public struct UserProfile: Codable {

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

     var levelScores: [LevelScore] = []
}

JSONEncoder() использовался для сохранения закодированного массива 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) {

loadedUsers не декодируется, если свойства, которые были у UserProfile при кодировании и сохранении, не содержат всех текущих свойств структуры UserProfile.

Любые предложения по обновлению сохраненных свойств структуры? Или я должен пройти долгий путь и создать заново, поскольку я еще не планировал заранее включить это свойство ранее?

Спасибо за любую помощь!


person RanLearns    schedule 02.01.2020    source источник
comment
Вы должны иметь возможность добавить свойство, если оно не является обязательным.   -  person Paulw11    schedule 02.01.2020


Ответы (1)


Как упоминалось в комментариях, вы можете сделать новое свойство необязательным, и тогда декодирование будет работать для старых данных.

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
    }
}
person Joakim Danielson    schedule 02.01.2020