@ObservedObjectをCodable変換する方法について解説します。
環境
この記事の情報は次のバージョンで動作確認しています。
【Xcode】11.6
【Swift】5.2.4
【iOS】13.6
【macOS】Catalina バージョン 10.15.6
【Swift】5.2.4
【iOS】13.6
【macOS】Catalina バージョン 10.15.6
概要
Codableを使って他の形式にデータ変換するには、対象のオブジェクトがCodableプロトコルに準拠している必要があります。
次に示すUserクラスは、3種類の型のプロパティを保有していますが、いずれの型もCodableプロトコルに準拠している為、クラス自体もCodable準拠となります。
1 2 3 4 5 6 7 8 9 10 11 |
class User: ObservableObject, Codable { var id = UUID() var name = "Capibara" var items = [5, 7, 10] init() { // 初期化処理 } } |
しかし、プロパティに@Publishedプロパティラッパーを付けると、Codable準拠でないと見なされエラーになってしまいます。
1 2 3 4 5 6 7 8 9 10 11 |
class User: ObservableObject, Codable { var id = UUID() var name = "Capibara" @Published var items = [5, 7, 10] init() { // 初期化処理 } } |
エラーメッセージ
1 2 3 4 |
Type 'User' does not conform to protocol 'Decodable' Type 'User' does not conform to protocol 'Encodable' |
これを回避するには、自分でCodableプロトコルに適用させる必要があります。
Codableプロトコルへの適用
クラスや構造体をCodableプロトコルに適用させるには、Swiftに次の3つの情報を示してやる必要があります。
- 対象プロイパティの指定
- 各プロパティのdecode(復号化)アクション
- 各プロパティのencode(コード化)アクション
最終的なコードを先に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
class User: ObservableObject, Codable { var id = UUID() var name = "Capibara" @Published var items = [5, 7, 10] init() { // 初期化処理 } /// ①変換対象プロパティ指定 enum CodingKeys: CodingKey { case id case name case items } /// ②プロパティのdecode(復号化)アクション required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(UUID.self, forKey: .id) name = try container.decode(String.self, forKey: .name) items = try container.decode([Int].self, forKey: .items) } /// ③プロパティのencode(コード化)アクション func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(name, forKey: .name) try container.encode(items, forKey: .items) } } |
①対象プロパティの指定
CodingKeyに準拠する列挙型(CodingKeys)を作成し、変換の対象とするプロパティをリスト化します。
1 2 3 4 5 6 7 |
enum CodingKeys: CodingKey { case id case name case items } |
②プロパティのdecode(復号化)アクション
各プロパティのdecodeアクションを定義します。
1行目と2行目は、そのままコピペでOK。
3行目以降で各プロパティのdecode処理を指定します。
decodeメソッドの第一引数で、各プロパティに合わせた"型名.self”を指定するのがポイントです。
1 2 3 4 5 6 7 8 |
required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(UUID.self, forKey: .id) name = try container.decode(String.self, forKey: .name) items = try container.decode([Int].self, forKey: .items) } |
③プロパティのencode(コード化)アクション
各プロパティのencodeアクションを定義します。
こちらも、1行目と2行目はコピペでOK。
3行目以降で各プロパティのencode処理を指定します。
1 2 3 4 5 6 7 8 |
func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(name, forKey: .name) try container.encode(items, forKey: .items) } |
以上で、対象のクラスや構造体がCodable準拠となり、コンパイルできるようになります。
あわせて読みたい記事
【Swift】Codableを使ったJSON変換
Codableを使ったJSON変換について解説します。
【SwiftUI】@ObservedObjectの使い方
(2020/11/28 更新) SwiftUIのデータバインディングの仕組みの一つで、データクラスの更新を監視する@ObservedObjectの使い方を解説します。 これを利用すると複数のViewから同一インスタンスへの同期が可能になりま...