こんにちは!リードエンジニアの@Utmrerです!ヴァイオレット・エヴァーガーデンが楽しみ過ぎて早く年越したいと毎日思っています。
この記事はコネヒト Advent Calendar 2017の18日目の記事です。
今日はCodableについて書くのですが、Swift4に追加されたCodableはとても便利で、JSONを構造体にマッピングするために使っている方が多いと思います。
Codableに準拠すればDataクラスへの変換を挟むことでUserDefaultsへ保存することができ、弊社では一部のデータモデルでその処理を行っています。
この記事ではUserDefaultsへのI/Fをサッと説明して、データモデルの変更(migration)についてもサッと説明します。
UserDefaultsへのCodableの保存
それではまずid
とname
を持つUserをCodableに準拠して定義します。
struct User: Codable {
let id: Int
let name: String
}
ではUserDefaultsに保存してみましょう。
let user = User(id: 1, name: "violet")
let data = try? JSONEncoder().encode(user)
UserDefaults.standard.set(data, forKey:"auto_memories_doll")
簡単ですね。では取り出してみましょう。
guard let data = UserDefaults.standard.data(forKey: "auto_memories_doll") else {
return
}
let user = try? JSONDecoder().decode(User.self, from: data)
簡単ですね。Decodeに失敗した時はnil
になります。
Userを変更する(migration)
ではここでUserに年齢を表すage
を追加したくなったとします。
struct User: Codable {
let id: Int
let name: String
let age: Int
}
このようにデータモデルが変更された時、UserDefaultsには変更される前の情報しか無く、Optionalではないage
の情報がないためUserDefaultsから以前保存したUserを取得出来なくなってしまいます。
// ageを追加しただけだと`user`がnilになっちゃう
let user = try? JSONDecoder().decode(User.self, from: data)
以前保存したid=1
, name=violet
というデータを失いたくない時はDecodableに定義されているinit(from decoder: Decoder)
を実装してあげましょう。
「UserDefaultsに保存されていない」が「ageはOptionalではない」ので、下記のように初期値を与えてあげるといいでしょう。
struct User: Codable {
enum CodingKeys: String, CodingKey {
case id
case name
case age
}
let id: Int
let name: String
let age: Int
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(Int.self, forKey: .id)
name = try values.decode(String.self, forKey: .name)
age = (try? values.decode(Int.self, forKey: .age)) ?? 0
}
}
まとめ
以前、自分のブログでSwift4のCodable(Decodable)でStringの値をIntにcastするというのを書いたのですが、Codableは「ちょっと頑張れば痒い所に手が届く」って感じですね。
以上、田村でした!明日は@super_mannerの「API Client ToolのPawのExtension周りの話」かもしれないし、違うかもしれません!
良いお年を!