# How does automatic Codable compliance work? For the sake of extending it

**URL:** <https://forums.swift.org/t/how-does-automatic-codable-compliance-work-for-the-sake-of-extending-it/25933>\
**Category:** Discussion\
**Tags:** codable\
**Created:** [June 16, 2019, 7:39pm UTC](https://forums.swift.org/t/how-does-automatic-codable-compliance-work-for-the-sake-of-extending-it/25933 "2019-06-16T19:39:16Z")\
**Posts on this page:** 1\
**Showing post:** 3

<div class="post-metadata">

**Author:** ![itaiferber](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/itaiferber/32/33840_2.png) [@itaiferber](https://forums.swift.org/u/itaiferber)\
**Post date:** [June 17, 2019, 5:07pm UTC](https://forums.swift.org/t/how-does-automatic-codable-compliance-work-for-the-sake-of-extending-it/25933/3 "2019-06-17T17:07:52Z")

</div>

You can indeed read the code to get the specifics, but to summarize the process more generally:

1. `Codable` synthesis (there are actually two passes — an `Encodable` synthesis pass, and a `Decodable` synthesis pass, but they largely share work) primarily uses the `CodingKeys` enum nested type on a type to influence how to implement `init(from:)` and `encode(to:)`
  - Specifically, if a `CodingKeys` type is not found, one will be synthesized
  - The process for synthesizing a `CodingKeys` enum involves iterating all stored properties on the owning type — each stored property is validated for `Encodable`/`Decodable` conformance, and if everything checks out, the `CodingKeys` enum gets a case with the same name as the property

2. Once the type is validated to have a `CodingKeys` enum (either via generation, or a user-supplied one), every case is checked against what the owning type has: every stored property needs to map 1-to-1 with an enum case (for `Encodable`, stored properties which don't have a matching enum case are explicitly not encoded in `encode(to:)`, but for `Decodable`, every stored property needs to be assigned an enum case)
3. Once this validation has passed, `encode(to:)` and `init(from:)` are generated by enumerating all of the cases in the `CodingKeys` enum, and generating an `encode`/`encodeIfPresent`/`decode`/`decodeIfPresent` call based on the type

Largely, the `CodingKeys` enum is the "source of truth" for what to encode and decode, and the enum cases (and their names) matter. To answer your more specific questions:

> [@CTMacUser](#):
>
> We can use period-separated member names as the string values, but what would the `enum` `case` s be called?

This would matter primarily if you're looking to make the tuple itself `Codable` and rely on the existing synthesis machinery to generate a conformance for it. Since tuples are not nominal, this wouldn't work anyway; instead, inside of `encode(to:)` and `init(from:)` generation, you could do something totally custom, making up custom names on the spot to encode into a nested container, for instance. There's flexibility here, assuming the tuple is checked for consistency up-front (e.g., all of its members are also `Codable` themselves)

> [@CTMacUser](#):
>
> Does it matter; can a user access the cases of a synthesized `CodingKey` type?

Yes, a type can access its own synthesized `CodingKeys` enum — by default the enum is synthesized as `private`, but if you implement your own `init(from:)`/`encode(to:)` [but not both], you will be able to access it and its cases by name.

* * *

One solution for this is to generate a `CodingKeys` type for the tuple nested somewhere/given a private name which contains auto-generated case names, but explicit `String` values which contain the actual key names you're looking for, e.g.

```swift
struct Foo : Codable {
    let bar: (String, Int)

    // Synthesized by Codable conformance:
    private enum CodingKeys: String, CodingKey {
        case bar
    }

    // Synthesized by additional tuple synthesis:
    private enum _bar_CodingKeys: String, CodingKey {
        case member0 = "0"
        case member1 = "1"
    }

    // Synthesized by Codable conformance with tuple additions:
    public func encode(to encoder: Encoder) throws {
        var container = try encoder.container(keyedBy: CodingKeys.self)
        var barContainer = try encoder.nestedContainer(keyedBy: _bar_CodingKeys.self, forKey: .bar)
        try barContainer.encode(bar.0, forKey: .member0)
        try barContainer.encode(bar.1, forKey: .member1)
    }
}

```

This would produce a result that looks like

```json
{ "bar": { "0": "Foo", "1": 42 } }

```

You can imagine that if the tuple members have labels, those labels are used:

- `(name: String, Int)` → `{ "name": "Foo", "1": 42 }`
- `(name: String, identifier: Int)` → ` { "name": "Foo", "identifier": 42 }`

There's also something to be said for ditching the member keys and just encoding an unkeyed container instead:

```swift
struct Foo : Codable {
    let bar: (String, Int)

    // Synthesized by Codable conformance:
    private enum CodingKeys: String, CodingKey {
        case bar
    }

    // Synthesized by Codable conformance with tuple additions:
    public func encode(to encoder: Encoder) throws {
        var container = try encoder.container(keyedBy: CodingKeys.self)
        var barContainer = try encoder.nestedUnkeyedContainer(forKey: .bar)
        try barContainer.encode(bar.0)
        try barContainer.encode(bar.1)
    }
}

```

This produces

```json
{ "bar": ["Foo", "42"] }

```

---

_[View the full topic](https://forums.swift.org/t/how-does-automatic-codable-compliance-work-for-the-sake-of-extending-it/25933)._
