LoginSignup
4
4

More than 1 year has passed since last update.

Swift Codableの公式解説をちょっと補足

Last updated at Posted at 2022-07-02

Codableについての公式の解説記事はこちらですが、JSONの具体例がなかったり読んでるだけだと若干わかりづらいところがあったので、自分なりに補足をつけてみたいと思います。主にCodingKeys周りを見ていきます。


具体的なJSONで理解する

解説記事のコードをまとめるとこんな感じです。デコード部分に絞ってます。

struct Coordinate {
    var latitude: Double
    var longitude: Double
    var elevation: Double

    enum CodingKeys: String, CodingKey {
        case latitude
        case longitude
        case additionalInfo
    }

    enum AdditionalInfoKeys: String, CodingKey {
        case elevation
    }
}

extension Coordinate: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        latitude = try values.decode(Double.self, forKey: .latitude)
        longitude = try values.decode(Double.self, forKey: .longitude)

        let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
        elevation = try additionalInfo.decode(Double.self, forKey: .elevation)
    }
}

解説記事には出てこないので分かりづらかったのですが、CoordinateはこういうJSONに対応しています。

{
    "latitude": 1.1,
    "longitude": 2.2,
    "additionalInfo": {
        "elevation": 3.3
    }
}

デコードするコードはこんな感じですね。

let json = """
{
    "latitude": 1.1,
    "longitude": 2.2,
    "additionalInfo": {
        "elevation": 3.3
    }
}
"""

let decoder = JSONDecoder()
let coordinate = try! decoder.decode(Coordinate.self, from: json.data(using: .utf8)!)
print(coordinate)

// 出力結果
// Coordinate(latitude: 1.1, longitude: 2.2)

CodingKeysという名前でなければいけないのか?

解説にはこのような記載があります。

Codable types can declare a special nested enumeration named CodingKeys that conforms to the CodingKey protocol.


上記の例でもCoordinate内部でCodingKeysというenumを宣言していますね。この名前を変えたらどうなるか試してみたいと思います。

まずはCodingKeysという名前で宣言した例です。

struct Coordinate: Decodable {
    var latitude: Double
    var longitude: Double

    enum CodingKeys: String, CodingKey {
        case latitude = "latitude_test"
        case longitude
    }
}

let json = """
{
    "latitude_test": 1.1,
    "longitude": 2.2,
}
"""

let decoder = JSONDecoder()
let coordinate = try! decoder.decode(Coordinate.self, from: json.data(using: .utf8)!)
print(coordinate)

// 結果
// Coordinate(latitude: 1.1, longitude: 2.2)

次に、CodingKeysをHogeKeysに変えてみます。すると、latitudeというキーが無いと怒られてしまいました。HogeKeysでは当然ながら認識されないようです。

struct Coordinate: Decodable {
    var latitude: Double
    var longitude: Double

    enum HogeKeys: String, CodingKey {
        case latitude = "latitude_test"
        case longitude
    }
}

let json = """
{
    "latitude_test": 1.1,
    "longitude": 2.2,
}
"""

let decoder = JSONDecoder()
let coordinate = try! decoder.decode(Coordinate.self, from: json.data(using: .utf8)!)
print(coordinate)

// エラー
// __lldb_expr_14/MyPlayground.playground:37: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "latitude", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: \"latitude\", intValue: nil) (\"latitude\").", underlyingError: nil))

HogeKeysという名前にする場合は、init(from:)を実装する必要があります。

struct Coordinate {
    var latitude: Double
    var longitude: Double

    enum HogeKeys: String, CodingKey {
        case latitude = "latitude_test"
        case longitude
    }
}

extension Coordinate: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: HogeKeys.self)
        latitude = try values.decode(Double.self, forKey: .latitude)
        longitude = try values.decode(Double.self, forKey: .longitude)
    }
}

let json = """
{
    "latitude_test": 1.1,
    "longitude": 2.2,
}
"""

let decoder = JSONDecoder()
let coordinate = try! decoder.decode(Coordinate.self, from: json.data(using: .utf8)!)
print(coordinate)

つまり、CodingKeysという名前にすることで、init(from:)を自動で実装してくれるんですね。
さらに、init(from:)が具体的にどういうことをやっているのかも最後の例でわかりました。

4
4
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
4
4