Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle model migration when store them in UserDefault?

I have an object called User that conforms to Codable introduced in Swift4.

for example, this object used to be

struct User: Codable {
    var firstName: String
}

and we use PropertyListDecoder().decode(User.self, from: data) and PropertyListEncoder().encode(value) to encode the User into Data and decode Data into User.

Now we updated the object to be

struct User: Codable {
    var firstName: String
    var isLoggedIn: Bool
}

If our app updated from the old app that has the old Data stored in UserDefault. The first thing app gonna do after update is fetch this Data and tries to decode into User by using PropertyListDecoder().decode(User.self, from: data). But, it gives an error:

po PropertyListDecoder().decode(User.self, from: data)
▿ DecodingError
  ▿ keyNotFound : 2 elements
    - .0 : CodingKeys(stringValue: "isLoggedIn", intValue: nil)
    ▿ .1 : Context
      - codingPath : 0 elements
      - debugDescription : "No value associated with key CodingKeys(stringValue: \"isLoggedIn\", intValue: nil) (\"isLoggedIn\")."
      - underlyingError : nil

Any idea how I would handle the model migration in this case? I know that for Coredata there's some easy ways to deal with this but I have no idea how to pull it off in UserDefault.

like image 217
Tony Lin Avatar asked Oct 16 '22 05:10

Tony Lin


1 Answers

You could implement the decode initializer and set a default value for isLoggedIn if there isn't any:

struct User: Codable {
  var firstName: String
  var isLoggedIn: Bool

  enum Keys: CodingKey {
    case firstName
    case isLoggedIn
  }

  public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    firstName = try container.decode(String.self, forKey: .firstName)
    isLoggedIn = try container.decodeIfPresent(Bool.self, forKey: .isLoggedIn) ?? false
  }
}
like image 61
alanpaivaa Avatar answered Oct 21 '22 06:10

alanpaivaa