49
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

コネヒトAdvent Calendar 2017

Day 18

UserDefaultsへのCodableの保存とmigration

Last updated at Posted at 2017-12-18

こんにちは!リードエンジニアの@Utmrerです!ヴァイオレット・エヴァーガーデンが楽しみ過ぎて早く年越したいと毎日思っています。
この記事はコネヒト Advent Calendar 2017の18日目の記事です。

今日はCodableについて書くのですが、Swift4に追加されたCodableはとても便利で、JSONを構造体にマッピングするために使っている方が多いと思います。
Codableに準拠すればDataクラスへの変換を挟むことでUserDefaultsへ保存することができ、弊社では一部のデータモデルでその処理を行っています。
この記事ではUserDefaultsへのI/Fをサッと説明して、データモデルの変更(migration)についてもサッと説明します。

UserDefaultsへのCodableの保存

それではまずidnameを持つUserをCodableに準拠して定義します。

User.swift
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を追加したくなったとします。

User.swift
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ではない」ので、下記のように初期値を与えてあげるといいでしょう。

User.swift
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周りの話」かもしれないし、違うかもしれません!
良いお年を!

コネヒト Advent Calendar 2017

49
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
37

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?