Sponsor Link
環境&対象
- macOS Monterery beta 3
- Xcode 13 beta3
- iOS 15 beta
以下のサンプルコードは、Playground で実行して確認することができます。
CodingKey の使われる場所
JSON 等の encoder/decoder をカスタマイズしようとすると登場してきます。
enum CodingKeys: Int, CodingKey {
case type1
case type2
}
上記のように定義すると、struct や class の encode/decode 時のキーとして使用することができます。
この時に、準拠する必要のある “CodingKey” とは? という点を説明します。
CodingKey の定義
encode/decode でキーとして使用することのできるタイプ (Protocol) として定義されています。
Apple のドキュメントは、こちら。
どうして、CodingKey に準拠するとキーとして使えるのか? 準拠しないとキーとして使えないのか? を最初のスタート地点としました。
encode/decode 時に言えることですが、わかりやすいので、JSON 表記を使って説明していきます。
調査に使う enum
以下のように enum を定義して、調べてみました。
// CodingKey に準拠
enum CodingKeys: CodingKey {
case type1
case type2
case type3
}
CodingKey protocol
CodingKey というプロトコルは、var intValue:Int? と var stringValue:String というプロパティを要求します。
いずれも、それを key として Dictionary を扱うことができると書かれているので、ユニークな Int や ユニークなString を提供することがあるということです。
上記で定義した enum は、特にプロトコルで要求するプロパティを実装していませんが、コンパイルできます。つまりデフォルトの実装が用意されているようです。
以下で、どのような実装か見てみます。
CodingKey に準拠した enum
enum CodingIntKeys: CodingKey {
case type1
case type3
case type2
}
let key1 = CodingKeys.type1
print(key1.intValue) // nil
print(key1.stringValue) // type1
let key2 = CodingKeys.type2
print(key2.intValue) // nil
print(key2.stringValue) // type2
上記の実行結果を見ると、intValue には、nil が stringValue には、enum の enumeration case が設定されるようです。
enum 内では、同一の名称を持つ enumeration case は、定義できませんので、この stringValue は dictionary の key としても使うことができます。
しかし、intValue には、いずれの enum 変数に対しても nil が返されるので、このままでは、intValue を dictionary の key には使えないようです。
CustomStringConvertible, CustomDebugStringConvertible
面白いことに、CodingKey は、CustomeStringConvertible と CustomDebugStringConvertible に準拠しています。
この2つの protocol は それぞれ、var description: String と var debugDescription: String というプロパティを要求しています。
ですので、以下のように、CodingKey に準拠した enum の変数をわかりやすい String 型に変換することができます。
CodingKey に準拠した型の description/debugDescription
enum CodingKeys: CodingKey {
case type1
case type2
case type3
}
let key1 = CodingKeys.type1
print(key1.description) // CodingKeys(stringValue: "type1", intValue: nil)
print(key1.debugDescription) // CodingKeys(stringValue: "type1", intValue: nil)
description/debugDescription 共に同じ String が返されてきました。
Encode への影響を確認してみる
Encode 時にどのように使用されているかを確認してみます。
まずは、デフォルト実装を使って JSON に encode してみます。
デフォルト実装を使った JSON への encode
struct MyStruct: Encodable {
var myValue: Int
enum CodingKeys: CodingKey {
case myValue
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(myValue, forKey: .myValue)
}
}
let data = MyStruct(myValue: 5)
let json = try! JSONEncoder().encode(data)
print(String(data: json, encoding: .utf8)!)
// output : {"myValue":5}
CodingKey で用意されているデフォルト実装の stringValue がキー値として使われていそうです。
独自実装した CodingKey を使って、JSON へ encode
CodingKey でデフォルト実装が提供されている stringValue を独自実装で上書きしてみます。
struct MyStruct: Encodable {struct MyStruct: Encodable {
var myValue: Int
enum CodingKeys: CodingKey {
case myValue
var stringValue: String {
switch self {
case .myValue:
return "modifiedMyValue"
}
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(myValue, forKey: .myValue)
}
}
let data = MyStruct(myValue: 5)
let json = try! JSONEncoder().encode(data)
print(String(data: json, encoding: .utf8)!)
// output : {"modifiedMyValue":5}
デフォルト実装を上書きすることで、自分で実装した stringValue がキー値として使用されることが確認できました。
不明点:intValue は何のため?
CodingKey だけに準拠すると intValue は、nil が返されるのですが、CodingKey と Int に準拠されると別のデフォルト実装が提供されるようになります。
CodingKey と Int に準拠した enum
CodingKey と Int に準拠させてチェックしてみます。
enum CodingIntKeys: Int, CodingKey {
case type1
case type3
case type2
}
let keyi1 = CodingIntKeys.type1
let keyi2 = CodingIntKeys.type2
print(keyi1.intValue) // Optional(0)
print(keyi1.stringValue) // type1
print(keyi2.intValue) // Optional(1)
print(keyi2.stringValue) // type2
intValue は、enum 内での定義順が返されるようです。(0スタートです)
試しに順序を変えてみると、intValue も変わります。
enum CodingIntKeys: Int, CodingKey {
case type3
case type1
case type2
}
let keyi1 = CodingIntKeys.type1
print(keyi1.intValue) // Optional(1)
print(keyi1.stringValue) // "type1"
デフォルト実装で intValue がどのように提供されるかは見えてきましたが、CodingKey として encode/decode にどのように関与しているかは、残念ながらわかりませんでした。
ご存じでしたら、教えてください。
まとめ:CodingKey に準拠させるということ
- stringValue プロパティが用意され、encode/decode 時のキーとして使用される
- デフォルト実装をオーバーライドすることで、使用するキー値を変更することができる
今回説明したことをベースに、encode/decode 時のキーを変更することが可能です。以下の記事で説明しています。
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link