# \[Pitch\] Allow coding of non-\`String\`/\`Int\` keyed \`Dictionary\` into a \`KeyedContainer\`

**URL:** <https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593>\
**Category:** Pitches\
**Created:** [February 9, 2021, 12:15pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593 "2021-02-09T12:15:55Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Morten\_Bek\_Ditlevsen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/morten_bek_ditlevsen/32/28133_2.png) [@Morten\_Bek\_Ditlevsen](https://forums.swift.org/u/Morten_Bek_Ditlevsen)\
**Post date:** [February 9, 2021, 12:15pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/1 "2021-02-09T12:15:56Z")

</div>

Hi all,  
I would like to present a pitch to solving an issue with the current state of `Dictionary` encoding and decoding.

Many thanks to @itaiferber for providing input and feedback, for revising the pitch and for helping me shape the overall direction.

* * *

**Pitch: Allow coding of non- `String` / `Int` keyed `Dictionary` into a `KeyedContainer`**

# Introduction

The current conformance of Swift's `Dictionary` to the `Codable` protocols has a somewhat-surprising limitation in that dictionaries whose key type is not `String` or `Int` (values directly representable in `CodingKey` types) encode not as `KeyedContainer`s but as `UnkeyedContainer`s. This behavior has caused much confusion for users and I would like to offer a way to improve the situation.

# Motivation

The primary motivation for this pitch lays in the much-discussed confusion of this default behavior:

- [Dictionarys encoding strategy](https://forums.swift.org/t/dictionarys-encoding-strategy/11973)
- [JSON Encoding / Decoding weird encoding of dictionary with enum values](https://forums.swift.org/t/json-encoding-decoding-weird-encoding-of-dictionary-with-enum-values/12995)
- [Bug or PEBKAC](https://forums.swift.org/t/bug-or-pebkac/33796)
- [Using RawRepresentable String and Int keys for Codable Dictionaries](https://forums.swift.org/t/using-rawrepresentable-string-and-int-keys-for-codable-dictionaries/26899)

The common situations where people have found the behavior confusing include:

- Using `enum`s as keys (especially when `RawRepresentable`, and backed by `String` or `Int` types)
- Using `String` wrappers (like the generic [Tagged](https://github.com/pointfreeco/swift-tagged) library or custom wrappers) as keys
- Using `Int8` or other `Int*` flavours as keys

In the various discussions, there are clear and concise explanations for this behavior, but it is also mentioned that supporting encoding of `RawRepresentable` `String` and `Int` keys into keyed containers may indeed be considered to be a bug, and is an oversight in the implementation ([JSON Encoding / Decoding weird encoding of dictionary with enum values, reply by Itai Ferber](https://forums.swift.org/t/json-encoding-decoding-weird-encoding-of-dictionary-with-enum-values/12995/7)).

There's a bug at [bugs.swift.org](http://bugs.swift.org) tracking the issue: [SR-7788](https://bugs.swift.org/browse/SR-7788)

Unfortunately, it is too late to change the behavior now:

1. It is a breaking change with respect to existing behavior, with backwards-compatibility ramifications (new code couldn't decode old data and vice versa), and
2. The behavior is tied to the Swift stdlib, so the behavior would differ between consumers of the code and what OS versions they are on

Instead, I propose the addition of a new protocol to the standard library. Opting in to this protocol for the key type of a `Dictionary` will allow the `Dictionary` to encode/decode to/from a `KeyedContainer`.

# Proposed Solution

I propose adding a new protocol to the standard library: `CodingKeyRepresentable`

Types conforming to `CodingKeyRepresentable` indicate that they can be represented by a `CodingKey` instance (which they can offer), allowing them to opt in to having dictionaries use their `CodingKey` representations in order to encode into `KeyedContainer`s.

The opt-in can only happen for a version of Swift where the protocol is available, so the user will be in full control of the situation. For instance I am currently using my own workaround, but once I only support iOS versions running a specific future Swift version with this feature, I could skip my own workaround and rely on this behavior instead.

I have a draft PR for the proposed solution: [#34458](https://github.com/apple/swift/pull/34458)

# Examples

```swift
// Same as stdlib's _DictionaryCodingKey
struct _AnyCodingKey: CodingKey {
    let stringValue: String
    let intValue: Int?
    
    init?(stringValue: String) {
        self.stringValue = stringValue
        self.intValue = Int(stringValue)
    }
    
    init?(intValue: Int) {
        self.stringValue = "\(intValue)"
        self.intValue = intValue
    }
}

struct ID: Hashable, CodingKeyConvertible {
    static let knownID1 = ID(stringValue: "<some-identifier-1>")
    static let knownID2 = ID(stringValue: "<some-identifier-2>")
    
    let stringValue: String
    
    var codingKey: CodingKey {
        return _AnyCodingKey(stringValue: stringValue)
    }
    
    init?(codingKey: CodingKey) {
        stringValue = codingKey.stringValue
    }
    
    init(stringValue: String) {
        self.stringValue = stringValue
    }
}

let data: [ID: String] = [
    .knownID1: "...",
    .knownID2: "...",
]

let encoder = JSONEncoder()
try String(data: encoder.encode(data), encoding: .utf8)

/*
{
    "<some-identifier-1>": "...",
    "<some-identifier-2>": "...",
}
*/

```

# Detailed Design

The proposed solution adds a new protocol, `CodingKeyRepresentable`:

```swift
/// Indicates that the conforming type can provide a `CodingKey` to be used when
/// encoding into a keyed container.
public protocol CodingKeyRepresentable {
    var codingKey: CodingKey { get }
    init?(codingKey: CodingKey)
}

```

In the conditional `Encodable` conformance on `Dictionary`, the following extra case can handle such conforming types:

```swift
    } else if let _ = Key.self as? CodingKeyRepresentable.Type {
      // Since the keys are CodingKeyRepresentable, we can use the `codingKey`
      // to create `_DictionaryCodingKey` instances.
      var container = encoder.container(keyedBy: _DictionaryCodingKey.self)
      for (key, value) in self.dict {
        let codingKey = (key as! CodingKeyRepresentable).codingKey
        let dictionaryCodingKey = _DictionaryCodingKey(codingKey: codingKey)
        try container.encode(value, forKey: dictionaryCodingKey)
      }
    } else {
      // Keys are Encodable but not Strings or Ints, so we cannot arbitrarily

```

In the conditional `Decodable` conformance on `Dictionary`, we can similarly handle conforming types:

```swift
    } else if let codingKeyRepresentableType = Key.self as? CodingKeyRepresentable.Type {
      // The keys are CodingKeyRepresentable, so we should be able to expect a keyed container.
      let container = try decoder.container(keyedBy: _DictionaryCodingKey.self)
      for key in container.allKeys {
        let value = try container.decode(Value.self, forKey: key)
        let k = codingKeyRepresentableType.init(codingKey: key)
        self.dict[k as! Key] = value
      }
    } else {
      // We should have encoded as an array of alternating key-value pairs.

```

# Impact on Existing Code

No direct impact, since adoption of this protocol is additive.

However, special care must be taken in _adopting_ the protocol, since adoption on any type `T` which has previously been encoded as a dictionary key can introduce backwards incompatibility with archives. It is always safe to adopt `CodingKeyConvertible` on new types, or types newly-conforming to `Codable`.

# Other Considerations

## Conforming stdlib types to `CodingKeyRepresentable`

Along the above lines, we do not propose conforming any existing stdlib or Foundation type to `CodingKeyRepresentable` due to backwards-compatibility concerns. Should end-user code require this conversion on existing types, we recommend writing wrapper types which conform on those types' behalf (for example, a `MyUUIDWrapper` which contains a `UUID` and conforms to `CodingKeyRepresentable` to allow using `UUID`s as dictionary keys directly).

## Adding an `AnyCodingKey` type to the standard library

Since types that conform to `CodingKeyRepresentable` will need to supply a `CodingKey`, most likely generated dynamically from type contents, this may be a good time to introduce a general key type which can take on any `String` or `Int` value it is initialized from.

`Dictionary` already uses exactly such a key type internally (`_DictionaryCodingKey`), as do `JSONEncoder` / `JSONDecoder` with `_JSONKey` (and `PropertyListEncoder` / `PropertyListDecoder` with `_PlistKey`), so generalization could be useful. The implementation of this type could match the implementation of `_AnyCodingKey` provided above.

# Alternatives Considered

## Why not just make the type conform to `CodingKey` directly?

For two reasons:

1. In the rare case in which a type already conforms to `CodingKey`, this runs the risk of behavior-breaking changes
2. `CodingKey` requires exposure of a `stringValue` and `intValue` property, which are only relevant when encoding and decoding; forcing types to expose these properties arbitrarily seems unreasonable

## Why not refine `RawRepresentable`, or use a `RawRepresentable where RawValue == CodingKey` constraint?

`RawRepresentable` conformance for types indicates a lossless conversion between the source type and its underlying `RawValue` type; this conversion is often the "canonical" conversion between a source type and its underlying representation, most commonly between `enum`s backed by raw values, and option sets similarly backed by raw values.

In contrast, we expect conversion to and from `CodingKey` to be _incidental_ , and representative only of the encoding and decoding process. We wouldn't suggest (or expect) a type's canonical underlying representation to be a `CodingKey`, which is what a `protocol CodingKeyRepresentable: RawRepresentable where RawValue == CodingKey` would require. Similarly, types which are already `RawRepresentable` with non- `CodingKey` raw values couldn't adopt conformance this way, and a big impetus for this feature is allowing `Int`- and `String`-backed `enum`s to participate as dictionary coding keys.

## Add workarounds to each `Encoder` / `Decoder`

Following a suggestion from @itaiferber, I have previously tried to provide a solution to this issue — not in general, but instead solving it by providing a `DictionaryKeyEncodingStrategy` for `JSONEncoder` : [#26257](https://github.com/apple/swift/pull/26257)

The idea there was to be able to express an opt-in to the new behavior directly in the `JSONEncoder` and `JSONDecoder` types by venting a new encoding/decoding 'strategy' configuration. I have since changed my personal opinion about this and I believe that the problem should not just be fixed for specific `Encoder` / `Decoder` pairs, but rather for all.

The implementation of this was not very pretty, involving casts and iterations over the dictionaries to be encoded/decoded.

## Await design of `newtype`

I have heard mentions of a `newtype` design, that basically tries to solve the issue that the [Tagged](https://github.com/pointfreeco/swift-tagged) library solves: namely creating type safe wrappers around other primitive types.

I am in no way an expert in this, and I don't know how this would be implemented, but _if_ it were possible to tell that `SomeType` is a `newtype` of `String`, then this could be used to provide a new implementation in the `Dictionary` `Codable` conformance, and since this feature does not exist in older versions of Swift (providing that this is a feature that requires changes to the Swift run-time), then adding this to the `Dictionary` `Codable` conformance would not be behavior breaking.

But those are an awful lot of ifs and buts, and it only solves one of the issues that people appear to run in to (the wrapping issue) — and not for instance `String` based enums or `Int8`-based keys.

## Do nothing

It is of course possible to handle this situation manually during encoding.

A rather unintrusive way of handling the situation is by using a property wrapper as suggested here: [CodableKey](https://forums.swift.org/t/bug-or-pebkac/33796/12).

This solution needs to be applied for each `Dictionary` and is a quite elegant workaround. But it is still a workaround for something that could be fixed in the stdlib.

---

<div class="post-metadata">

**Author:** ![SwiftUI](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/swiftui/32/24361_2.png) [@SwiftUI](https://forums.swift.org/u/SwiftUI)\
**Post date:** [February 9, 2021, 1:16pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/2 "2021-02-09T13:16:14Z")

</div>

I feel the `Codable` property wrapper is a better way

---

<div class="post-metadata">

**Author:** ![Morten\_Bek\_Ditlevsen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/morten_bek_ditlevsen/32/28133_2.png) [@Morten\_Bek\_Ditlevsen](https://forums.swift.org/u/Morten_Bek_Ditlevsen)\
**Post date:** [February 18, 2021, 6:59am UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/3 "2021-02-18T06:59:25Z")

</div>

Hi BigSur,  
Thanks for your comment.  
As I am gathering feedback for future directions, I would like to hear if you can elaborate on your opinion a bit?

And if any of the people liking the post have any comments, they too are more than welcome.

I am also taking the liberty of tagging some of the commenters of the previous discussions mentioned in the pitch with hopes of getting additional feedback:  
@cherrywoods @zarghol @ahti @Jon_Shier @hartbit @acecilia @AlexanderM @Lantua @Martin @SDGGiesbrecht

---

<div class="post-metadata">

**Author:** ![Morten\_Bek\_Ditlevsen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/morten_bek_ditlevsen/32/28133_2.png) [@Morten\_Bek\_Ditlevsen](https://forums.swift.org/u/Morten_Bek_Ditlevsen)\
**Post date:** [February 18, 2021, 7:00am UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/4 "2021-02-18T07:00:15Z")

</div>

@Karl @jjanke @ffried @WAHa.06x36

---

<div class="post-metadata">

**Author:** ![ffried](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ffried/32/29232_2.png) [@ffried](https://forums.swift.org/u/ffried)\
**Post date:** [February 18, 2021, 7:46am UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/5 "2021-02-18T07:46:30Z")

</div>

@Morten_Bek_Ditlevsen Thanks for the pitch and the ping!

I really like the idea of the `CodingKeyRepresentable` protocol - I've even done something similar in one of my projects.

I have a few remarks:

## Associated Type for `CodingKey`

Given that `CodingKeyRepresentable` follows `RawRepresentable`, I think it should gain an `associatedType` for its `CodingKey`. This will break the direct cast, but it's still doable by e.g. making `_DictionaryCodingKey` generic over `Dictionary.Key` and then having a conditional conformance to a protocol that extracts the type-erased `CodingKey` internally. IMHO it's worth it given that implementations are then able to better decide whether to return `nil` from `init(codingKey:)`.  
To show this, here's an example:

```swift
enum MyKey: Int, CodingKey {
    case a = 1
    case b = 3
    case c = 5

    var intValue: Int? { rawValue }

    var stringValue: String {
        switch self {
        case .a: return "a"
        case .b: return "b"
        case .c: return "c"
        }
    }

    init?(intValue: Int) { self.init(rawValue: intValue) }

    init?(stringValue: String) {
        guard let rawValue = RawValue(stringValue) else { return nil }
        self.init(rawValue: rawValue)
    }
}

struct MyCustomType: CodingKeyRepresentable {
    typealias CodingKey = MyKey

    var useB = false

    var codingKey: CodingKey {
        useB ? .b : .a
    }

    init?(codingKey: CodingKey) {
        switch codingKey {
        case .a: useB = false
        case .b: useB = true
        case .c: return nil // .c is unsupported
        }
    }
}

```

If there's no associated type for `CodingKey`, things get a bit more complicated inside our `init`:

```swift
init?(codingKey: CodingKey) {
    // oh no, this fails because `codingKey` is actually `_DictionaryCodingKey`...
    guard let key = codingKey as? MyKey else { return nil }

    // This would work, but needs to be done manually every time.
    guard let key = MyKey(intValue: codingKey.intValue) else { return nil }

    switch key { /* ... */ }
}

```

IMHO it's counter-intuitive that you return `MyKey` from the `codingKey` accessor, but get a totally different `codingKey` into the `init(codingKey:)` initializer.

## `AnyCodingKey`

I'm unsure about the `AnyCodingKey` type. One the one hand, I totally agree that there will be tons of similar implementations. On the other hand it could result in people missing out on the advantages of having their own key type (as in my example before) by simply using the `AnyCodingKey` type.  
However, in the end, I think I'd still vote for adding `AnyCodingKey`.

## `CodableKey`

The property wrapper solution is definitively a nice idea, but I think it has some major drawbacks:

- Using `Int8` (or any other numeric stdlib type for that matter) as key requires it to conform to `CodingKey`. This conformance would have to come from the stdlib to prevent conformance collisions across e.g. Swift packages. And IMHO those types shouldn't provide a `CodingKey` conformance per se...
- It's not straightforward to simply encode/decode e.g. a `Dictionary<Int8, String>` that is not a property of another `Codable` type (also mentioned in the example in the linked post).
- It's impossible to _add_ a `Codable` conformance to an object that is already defined. So if I define a struct (`MyType`) having a `Dictionary<Int8, String>` in one file, I can't simply put an `extension MyType: Codable { /* ... */ }` into another file.
- Currently, there can only be one property wrapper per property. While this limitation is likely to be lifted (I don't know the current state here), it currently means that every wrapper adds a potential limitation.

Sorry for the long post. Let me know what you think!

---

<div class="post-metadata">

**Author:** ![cherrywoods](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cherrywoods/32/2444_2.png) [@cherrywoods](https://forums.swift.org/u/cherrywoods)\
**Post date:** [February 18, 2021, 2:44pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/6 "2021-02-18T14:44:38Z")

</div>

Hi there,  
I think this is really a nice solution to the outlined problem. I think it is sensible adding an `_AnyCodingKey` type, because that is needed quite often as previously mentioned.

What's unfortunate is that this was not handled back then when it was still acceptable to change the encoding and decoding behavior. But since that opportunity has gone, I think this proposal makes sense. It will still be unintuitive why key types that are e.g. string representible are not directly encoded as string keys in keyed containers, but now there is at least an easy fix to this.

The suggestion by @ffried to add an associated type for the `CodingKey` of a coding key representable sounds good to me. Still I would add an `_AnyCodingKey` type, I have also previously written something like this, if remember correctly.

Thanks for writing the pitch @Morten_Bek_Ditlevsen!

---

<div class="post-metadata">

**Author:** ![Morten\_Bek\_Ditlevsen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/morten_bek_ditlevsen/32/28133_2.png) [@Morten\_Bek\_Ditlevsen](https://forums.swift.org/u/Morten_Bek_Ditlevsen)\
**Post date:** [February 21, 2021, 7:41am UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/7 "2021-02-21T07:41:43Z")

</div>

Thank you both so much for the excellent feedback!

I had initially not imagined a use case for having the `CodingKey` type be an associated value, but you make a compelling argument - and the only ‘cost’ appears to be the slight extra work in the implementation, and here you provided a good solution, so thanks for that, @ffried ! 🙂

Also thank you for the analysis of the possible drawbacks of using the propertyWrapper solution - I’ll add those to the pitch text. I do think that you can already compose property wrappers, however, so perhaps I’ll skip that argument. 🙂

Thanks again for the comments! I’ll return with a modified pitch text and a revised PR.

---

<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:** [February 21, 2021, 2:19pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/9 "2021-02-21T14:19:08Z")

</div>

This is some excellent feedback! One thing I wanted to respond to:

> [@ffried](#):
>
> ## Associated Type for `CodingKey`
> 
> Given that `CodingKeyRepresentable` follows `RawRepresentable` , I think it should gain an `associatedType` for its `CodingKey` .

When @Morten_Bek_Ditlevsen and I discussed some of the topics here originally, this subject came up and I suggested that `CodingKeyRepresentable` not have an `associatedtype` — I wanted to share some of those reasons for posterity, and in case they might influence the decision of where to go here.

Because `associatedtype`s have non-zero cost on the consuming side (e.g. checking for `CodingKeyRepresentable` conformance, using the key type), I think that the associated type definition would need to carry its weight. Despite the name, I think that the key difference between `CodingKeyRepresentable` and `RawRepresentable` is that the _identity_ of the `RawValue` type is crucial to `RawRepresentable`, but not so in the `CodingKeyRepresentable` case.

On the _consuming_ side of `CodingKeyRepresentable.codingKey` (e.g. in `Dictionary`), I don't believe key type identity is necessarily useful enough:

- The main use for the `.codingKey` value is immediate retrieval of the underlying `String`/`Int` values. `Dictionary` would either pull those values out for immediate use and throw away the original key
- In a non-generic context (or even one not predicated on `CodingKeyRepresentable` conformance), you can't meaningfully get at the key type. The type-erasure song and dance you have to do to get the key values won't be able to hand you a typed key (and the pain of doing that dance is that because it doesn't make sense to expose a public protocol for doing the erasure, every consumer that wants to do this needs to reinvent the wheel and add another protocol for doing it; we had to do it a few times for `Optional`s and it's a bit of a shame)
- Even if it were necessary to get a meaningful key type, the majority use-case for this feature, I believe, will be to provide dynamic-value keys for non-enumerable types (e.g. `struct`s like `UUID` [though yes, we can't make it conform]); for these types, you can't necessarily define a `CodingKey`s _`enum`_ and instead, you'd likely want to use a more generic key type like `AnyCodingKey` (which by definition doesn't have identity)

On the _producing_ side (e.g. in `MyCustomType`), I'm also not sure the utility is necessarily enough: in general, the majority of `CodingKeyRepresentable` types (I believe) will only really care about the `String`/`Int` values of the keys, since they will be initialized dynamically (again, I think of `UUID` initialization from a `CodingKey.stringValue` — you can do this from _any_ `CodingKey`).

I believe that the constrained `MyKey` example above will be the minority use-case, but expressed without the `associatedtype` constraint too:

```swift
enum MyKey: Int, CodingKey {
    case a = 1, b = 3, c = 5

    // There are several ways to express this, just an example:
    init?(codingKey: CodingKey) {
        if let key = codingKey.intValue.flatMap(Self.init(intValue:)) {
            self = key
        } else if let key = Self(stringValue: codingKey.stringValue) {
            self = key
        } else {
            return nil
        }
    }
}

struct MyCustomType: CodingKeyRepresentable {
    var useB = false

    var codingKey: CodingKey {
        useB ? MyKey.b : MyKey.a
    }

    init?(codingKey: CodingKey) {
        switch MyKey(codingKey: codingKey) {
        case .a: useB = false
        case .b: useB = true
        default: return nil
        }    
    }
}

```

I personally find this equally as expressive, and I think that not requiring the associated type gives more flexibility without a significant loss, especially with non-`enum` types in mind. What do you think? (If `MyCustomType` here was inspired by a real-world example, I'd love to know more about it!)

---

<div class="post-metadata">

**Author:** ![Morten\_Bek\_Ditlevsen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/morten_bek_ditlevsen/32/28133_2.png) [@Morten\_Bek\_Ditlevsen](https://forums.swift.org/u/Morten_Bek_Ditlevsen)\
**Post date:** [February 26, 2021, 8:37am UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/10 "2021-02-26T08:37:58Z")

</div>

Hi all,  
Thank you for the input - I have updated the pitch text to reflect the discussion above.

- Add discussion about using an associated type for the `CodingKey` of the `CodingKeyRepresentable` type.

- Add drawbacks to the property wrapper workaround.

Here is the updated pitch text:

**Pitch: Allow coding of non- `String` / `Int` keyed `Dictionary` into a `KeyedContainer`**

# Introduction

The current conformance of Swift's `Dictionary` to the `Codable` protocols has a somewhat-surprising limitation in that dictionaries whose key type is not `String` or `Int` (values directly representable in `CodingKey` types) encode not as `KeyedContainer`s but as `UnkeyedContainer`s. This behavior has caused much confusion for users and I would like to offer a way to improve the situation.

# Motivation

The primary motivation for this pitch lays in the much-discussed confusion of this default behavior:

- [Dictionarys encoding strategy](https://forums.swift.org/t/dictionarys-encoding-strategy/11973)
- [JSON Encoding / Decoding weird encoding of dictionary with enum values](https://forums.swift.org/t/json-encoding-decoding-weird-encoding-of-dictionary-with-enum-values/12995)
- [Bug or PEBKAC](https://forums.swift.org/t/bug-or-pebkac/33796)
- [Using RawRepresentable String and Int keys for Codable Dictionaries](https://forums.swift.org/t/using-rawrepresentable-string-and-int-keys-for-codable-dictionaries/26899)

The common situations where people have found the behavior confusing include:

- Using `enum`s as keys (especially when `RawRepresentable`, and backed by `String` or `Int` types)
- Using `String` wrappers (like the generic [Tagged](https://github.com/pointfreeco/swift-tagged) library or custom wrappers) as keys
- Using `Int8` or other `Int*` flavours as keys

In the various discussions, there are clear and concise explanations for this behavior, but it is also mentioned that supporting encoding of `RawRepresentable` `String` and `Int` keys into keyed containers may indeed be considered to be a bug, and is an oversight in the implementation ([JSON Encoding / Decoding weird encoding of dictionary with enum values, reply by Itai Ferber](https://forums.swift.org/t/json-encoding-decoding-weird-encoding-of-dictionary-with-enum-values/12995/7)).

There's a bug at [bugs.swift.org](http://bugs.swift.org) tracking the issue: [SR-7788](https://bugs.swift.org/browse/SR-7788)

Unfortunately, it is too late to change the behavior now:

1. It is a breaking change with respect to existing behavior, with backwards-compatibility ramifications (new code couldn't decode old data and vice versa), and
2. The behavior is tied to the Swift stdlib, so the behavior would differ between consumers of the code and what OS versions they are on

Instead, I propose the addition of a new protocol to the standard library. Opting in to this protocol for the key type of a `Dictionary` will allow the `Dictionary` to encode/decode to/from a `KeyedContainer`.

# Proposed Solution

I propose adding a new protocol to the standard library: `CodingKeyRepresentable`

Types conforming to `CodingKeyRepresentable` indicate that they can be represented by a `CodingKey` instance (which they can offer), allowing them to opt in to having dictionaries use their `CodingKey` representations in order to encode into `KeyedContainer`s.

The opt-in can only happen for a version of Swift where the protocol is available, so the user will be in full control of the situation. For instance I am currently using my own workaround, but once I only support iOS versions running a specific future Swift version with this feature, I could skip my own workaround and rely on this behavior instead.

I have a draft PR for the proposed solution: [#34458](https://github.com/apple/swift/pull/34458)

# Examples

```swift
// Same as stdlib's _DictionaryCodingKey
struct _AnyCodingKey: CodingKey {
    let stringValue: String
    let intValue: Int?
    
    init?(stringValue: String) {
        self.stringValue = stringValue
        self.intValue = Int(stringValue)
    }
    
    init?(intValue: Int) {
        self.stringValue = "\(intValue)"
        self.intValue = intValue
    }
}

struct ID: Hashable, CodingKeyConvertible {
    static let knownID1 = ID(stringValue: "<some-identifier-1>")
    static let knownID2 = ID(stringValue: "<some-identifier-2>")
    
    let stringValue: String
    
    var codingKey: CodingKey {
        return _AnyCodingKey(stringValue: stringValue)
    }
    
    init?(codingKey: CodingKey) {
        stringValue = codingKey.stringValue
    }
    
    init(stringValue: String) {
        self.stringValue = stringValue
    }
}

let data: [ID: String] = [
    .knownID1: "...",
    .knownID2: "...",
]

let encoder = JSONEncoder()
try String(data: encoder.encode(data), encoding: .utf8)

/*
{
    "<some-identifier-1>": "...",
    "<some-identifier-2>": "...",
}
*/

```

# Detailed Design

The proposed solution adds a new protocol, `CodingKeyRepresentable`:

```swift
/// Indicates that the conforming type can provide a `CodingKey` to be used when
/// encoding into a keyed container.
public protocol CodingKeyRepresentable {
    var codingKey: CodingKey { get }
    init?(codingKey: CodingKey)
}

```

In the conditional `Encodable` conformance on `Dictionary`, the following extra case can handle such conforming types:

```swift
    } else if let _ = Key.self as? CodingKeyRepresentable.Type {
      // Since the keys are CodingKeyRepresentable, we can use the `codingKey`
      // to create `_DictionaryCodingKey` instances.
      var container = encoder.container(keyedBy: _DictionaryCodingKey.self)
      for (key, value) in self.dict {
        let codingKey = (key as! CodingKeyRepresentable).codingKey
        let dictionaryCodingKey = _DictionaryCodingKey(codingKey: codingKey)
        try container.encode(value, forKey: dictionaryCodingKey)
      }
    } else {
      // Keys are Encodable but not Strings or Ints, so we cannot arbitrarily

```

In the conditional `Decodable` conformance on `Dictionary`, we can similarly handle conforming types:

```swift
    } else if let codingKeyRepresentableType = Key.self as? CodingKeyRepresentable.Type {
      // The keys are CodingKeyRepresentable, so we should be able to expect a keyed container.
      let container = try decoder.container(keyedBy: _DictionaryCodingKey.self)
      for key in container.allKeys {
        let value = try container.decode(Value.self, forKey: key)
        let k = codingKeyRepresentableType.init(codingKey: key)
        self.dict[k as! Key] = value
      }
    } else {
      // We should have encoded as an array of alternating key-value pairs.

```

# Impact on Existing Code

No direct impact, since adoption of this protocol is additive.

However, special care must be taken in _adopting_ the protocol, since adoption on any type `T` which has previously been encoded as a dictionary key can introduce backwards incompatibility with archives. It is always safe to adopt `CodingKeyConvertible` on new types, or types newly-conforming to `Codable`.

# Other Considerations

## Conforming stdlib types to `CodingKeyRepresentable`

Along the above lines, we do not propose conforming any existing stdlib or Foundation type to `CodingKeyRepresentable` due to backwards-compatibility concerns. Should end-user code require this conversion on existing types, we recommend writing wrapper types which conform on those types' behalf (for example, a `MyUUIDWrapper` which contains a `UUID` and conforms to `CodingKeyRepresentable` to allow using `UUID`s as dictionary keys directly).

## Adding an `AnyCodingKey` type to the standard library

Since types that conform to `CodingKeyRepresentable` will need to supply a `CodingKey`, most likely generated dynamically from type contents, this may be a good time to introduce a general key type which can take on any `String` or `Int` value it is initialized from.

`Dictionary` already uses exactly such a key type internally (`_DictionaryCodingKey`), as do `JSONEncoder` / `JSONDecoder` with `_JSONKey` (and `PropertyListEncoder` / `PropertyListDecoder` with `_PlistKey`), so generalization could be useful. The implementation of this type could match the implementation of `_AnyCodingKey` provided above.

# Alternatives Considered

## Why not just make the type conform to `CodingKey` directly?

For two reasons:

1. In the rare case in which a type already conforms to `CodingKey`, this runs the risk of behavior-breaking changes
2. `CodingKey` requires exposure of a `stringValue` and `intValue` property, which are only relevant when encoding and decoding; forcing types to expose these properties arbitrarily seems unreasonable

## Why not refine `RawRepresentable`, or use a `RawRepresentable where RawValue == CodingKey` constraint?

`RawRepresentable` conformance for types indicates a lossless conversion between the source type and its underlying `RawValue` type; this conversion is often the "canonical" conversion between a source type and its underlying representation, most commonly between `enum`s backed by raw values, and option sets similarly backed by raw values.

In contrast, we expect conversion to and from `CodingKey` to be _incidental_ , and representative only of the encoding and decoding process. We wouldn't suggest (or expect) a type's canonical underlying representation to be a `CodingKey`, which is what a `protocol CodingKeyRepresentable: RawRepresentable where RawValue == CodingKey` would require. Similarly, types which are already `RawRepresentable` with non- `CodingKey` raw values couldn't adopt conformance this way, and a big impetus for this feature is allowing `Int`- and `String`-backed `enum`s to participate as dictionary coding keys.

## Why not use an Associated Type for `CodingKey`

It was suggested during the pitch phase to use an associated type for the `CodingKey` in the `CodingKeyRepresentable` protocol.

The presented use case was perfectly valid - and demonstrated using the following example:

```swift
enum MyKey: Int, CodingKey {
    case a = 1
    case b = 3
    case c = 5

    var intValue: Int? { rawValue }

    var stringValue: String {
        switch self {
        case .a: return "a"
        case .b: return "b"
        case .c: return "c"
        }
    }

    init?(intValue: Int) { self.init(rawValue: intValue) }

    init?(stringValue: String) {
        guard let rawValue = RawValue(stringValue) else { return nil }
        self.init(rawValue: rawValue)
    }
}

struct MyCustomType: CodingKeyRepresentable {
    typealias CodingKey = MyKey

    var useB = false

    var codingKey: CodingKey {
        useB ? .b : .a
    }

    init?(codingKey: CodingKey) {
        switch codingKey {
        case .a: useB = false
        case .b: useB = true
        case .c: return nil // .c is unsupported
        }
    }
}

```

An analysis of this suggestion hints that the non-zero cost of doing type erasure for pulling out the key values at the consuming site might not carry it's weight ([https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/9](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/9)):

Because `associatedtype` s have non-zero cost on the consuming side (e.g. checking for `CodingKeyRepresentable` conformance, using the key type), I think that the associated type definition would need to carry its weight. Despite the name, I think that the key difference between `CodingKeyRepresentable` and `RawRepresentable` is that the _identity_ of the `RawValue` type is crucial to `RawRepresentable` , but not so in the `CodingKeyRepresentable` case.

On the _consuming_ side of `CodingKeyRepresentable.codingKey` (e.g. in `Dictionary` ), I don't believe key type identity is necessarily useful enough:

- The main use for the `.codingKey` value is immediate retrieval of the underlying `String` / `Int` values. `Dictionary` would either pull those values out for immediate use and throw away the original key
- In a non-generic context (or even one not predicated on `CodingKeyRepresentable` conformance), you can't meaningfully get at the key type. The type-erasure song and dance you have to do to get the key values won't be able to hand you a typed key (and the pain of doing that dance is that because it doesn't make sense to expose a public protocol for doing the erasure, every consumer that wants to do this needs to reinvent the wheel and add another protocol for doing it; we had to do it a few times for `Optional` s and it's a bit of a shame)
- Even if it were necessary to get a meaningful key type, the majority use-case for this feature, I believe, will be to provide dynamic-value keys for non-enumerable types (e.g. `struct` s like `UUID` [though yes, we can't make it conform]); for these types, you can't necessarily define a `CodingKey` s _`enum`_ and instead, you'd likely want to use a more generic key type like `AnyCodingKey` (which by definition doesn't have identity)

On the _producing_ side (e.g. in `MyCustomType` ), I'm also not sure the utility is necessarily enough: in general, the majority of `CodingKeyRepresentable` types (I believe) will only really care about the `String` / `Int` values of the keys, since they will be initialized dynamically (again, I think of `UUID` initialization from a `CodingKey.stringValue` — you can do this from _any_ `CodingKey` ).

I believe that the constrained `MyKey` example above will be the minority use-case, but expressed without the `associatedtype` constraint too:

```swift
enum MyKey: Int, CodingKey {
    case a = 1, b = 3, c = 5

    // There are several ways to express this, just an example:
    init?(codingKey: CodingKey) {
        if let key = codingKey.intValue.flatMap(Self.init(intValue:)) {
            self = key
        } else if let key = Self(stringValue: codingKey.stringValue) {
            self = key
        } else {
            return nil
        }
    }
}

struct MyCustomType: CodingKeyRepresentable {
    var useB = false

    var codingKey: CodingKey {
        useB ? MyKey.b : MyKey.a
    }

    init?(codingKey: CodingKey) {
        switch MyKey(codingKey: codingKey) {
        case .a: useB = false
        case .b: useB = true
        default: return nil
        }    
    }
}

```

I personally find this equally as expressive, and I think that not requiring the associated type gives more flexibility without a significant loss, especially with non- `enum` types in mind.

## Add workarounds to each `Encoder` / `Decoder`

Following a suggestion from @itaiferber, I have previously tried to provide a solution to this issue — not in general, but instead solving it by providing a `DictionaryKeyEncodingStrategy` for `JSONEncoder` : [#26257](https://github.com/apple/swift/pull/26257)

The idea there was to be able to express an opt-in to the new behavior directly in the `JSONEncoder` and `JSONDecoder` types by venting a new encoding/decoding 'strategy' configuration. I have since changed my personal opinion about this and I believe that the problem should not just be fixed for specific `Encoder` / `Decoder` pairs, but rather for all.

The implementation of this was not very pretty, involving casts and iterations over the dictionaries to be encoded/decoded.

## Await design of `newtype`

I have heard mentions of a `newtype` design, that basically tries to solve the issue that the [Tagged](https://github.com/pointfreeco/swift-tagged) library solves: namely creating type safe wrappers around other primitive types.

I am in no way an expert in this, and I don't know how this would be implemented, but _if_ it were possible to tell that `SomeType` is a `newtype` of `String`, then this could be used to provide a new implementation in the `Dictionary` `Codable` conformance, and since this feature does not exist in older versions of Swift (providing that this is a feature that requires changes to the Swift run-time), then adding this to the `Dictionary` `Codable` conformance would not be behavior breaking.

But those are an awful lot of ifs and buts, and it only solves one of the issues that people appear to run in to (the wrapping issue) — and not for instance `String` based enums or `Int8`-based keys.

## Do nothing

It is of course possible to handle this situation manually during encoding.

A rather unintrusive way of handling the situation is by using a property wrapper as suggested here: [CodableKey](https://forums.swift.org/t/bug-or-pebkac/33796/12).

This solution needs to be applied for each `Dictionary` and is a quite elegant workaround. But it is still a workaround for something that could be fixed in the stdlib.

A few drawbacks to the property wrapper solution were given during the pitch phase:

- Using `Int8` (or any other numeric stdlib type for that matter) as key requires it to conform to `CodingKey` . This conformance would have to come from the stdlib to prevent conformance collisions across e.g. Swift packages. And IMHO those types shouldn't provide a `CodingKey` conformance per se...
- It's not straightforward to simply encode/decode e.g. a `Dictionary<Int8, String>` that is not a property of another `Codable` type (also mentioned in the example in the linked post).
- It's impossible to _add_ a `Codable` conformance to an object that is already defined. So if I define a struct ( `MyType` ) having a `Dictionary<Int8, String>` in one file, I can't simply put an `extension MyType: Codable { /* ... */ }` into another file.

---

<div class="post-metadata">

**Author:** ![Tony\_Parker](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tony_parker/32/273_2.png) [@Tony\_Parker](https://forums.swift.org/u/Tony_Parker)\
**Post date:** [May 15, 2021, 5:22pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/11 "2021-05-15T17:22:07Z")

</div>

I think this proposal is going in the right direction. The main question I have is the CodingKey return type. I’m somewhat on the fence about this. I’m not sure we’ve improved the status quo by making everyone reimplement AnyCodingKey; but I also don’t think it’s necessarily the case that we want to introduce a new erased type into the stdlib.

I wonder if perhaps this protocol should just ask for a string key value directly, and have another function which returns Int?, but with a default implementation that returns nil. Then the most common implementation of the protocol is just returning whatever String you want to use as a key directly.

---

<div class="post-metadata">

**Author:** ![Morten\_Bek\_Ditlevsen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/morten_bek_ditlevsen/32/28133_2.png) [@Morten\_Bek\_Ditlevsen](https://forums.swift.org/u/Morten_Bek_Ditlevsen)\
**Post date:** [May 17, 2021, 6:42am UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/12 "2021-05-17T06:42:59Z")

</div>

Thanks for the feedback, @Tony_Parker !

There are a few reasons that the protocol uses `CodingKey` as it's 'currency': For one, `CodingKey` carries some semantics that `String` and `Int` does not, so it may be easier to explain/understand the purpose of the protocol by using the `CodingKeyRepresentable` name and the `CodingKey` as the type of the wrapped key and as input for the failable initializer.

Secondly `CodingKey` exactly encapsulated the fact that a key may be represented as an `Int` or a `String`.

Thirdly (and perhaps a bit weakly founded): Using the `CodingKey` type and the name `codingKey` as the property name, I would think that the conforming to the new protocol has a very low chance of clashing with existing functionality. Depending on the shape and naming in the alternative, this might not be the case.

I totally get that you are not keen on getting `AnyCodingKey` in the stdlib, and I also understand the worry about everybody having to implement their own version of it.

If we explore the idea of using `String` and `Int` directly instead of `CodingKey` then we have a question about naming. Is `CodingKeyRepresentable` still a good name?

```swift
/// Indicates that the conforming type can provide a `CodingKey` to be used when
/// encoding into a keyed container.
public protocol CodingKeyRepresentable {
    var stringKey: String { get }
    var intKey: Int? { get }
    init?(stringKey: String)
    init?(intKey: Int)
}

```

Then the implementation of the `Dictionary` encoding and decoding would first attempt the `Int` based version and then the `String` based version, right?

This could of course work.

Another idea would be to split it into two protocols, one for the `String`-based key and another for the `Int`-based key. E.g. `IntKeyRepresentable` and `StringKeyRepresentable`. But then you lose a small bit of context since you don't specify the domain of keys. Could of course be `IntCodingKeyRepresentable`, but it's not actually dealing with `CodingKey`, so that may also be misguiding.

All in all my preference is still the `CodingKeyRepresentable` interface - even though it has the cost of some extra boilerplate. For my own part, my main purpose of using it is in a context of the `Tagged` library [https://github.com/pointfreeco/swift-tagged](https://github.com/pointfreeco/swift-tagged) (or similar) - where a `String` is wrapped in some generic in order to improve type safety of things like identifiers and the like.

@itaiferber do you have any additional thoughts about using `CodingKey` vs. raw `String` and `Int` keys here?

---

<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:** [May 17, 2021, 7:35pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/13 "2021-05-17T19:35:50Z")

</div>

> [@Morten\_Bek\_Ditlevsen](#):
>
> @itaiferber do you have any additional thoughts about using `CodingKey` vs. raw `String` and `Int` keys here?

I think your initial points capture my personal feelings pretty exactly. I think dealing with `CodingKey` directly as the currency type keeps this narrow in scope, without muddying the waters. It's more direct about its purpose, and slightly less likely to conflict with existing or new properties. (I also like that offering a whole `CodingKey` value makes it more difficult to forget about the possibility of offering integer keys, which might be easier to do if the `intKey` requirements are automatically fulfilled, or if the protocol is split in two.)

That being said, I understand the hesitance about introducing a new stdlib type for this purpose. I can see leaving it out initially and introducing it later on if it becomes clearly necessary. (I will say that at the very least, there are existing implementations that could benefit from this introduction (`Dictionary`, `JSONEncoder`/`JSONDecoder`, `PropertyListEncoder`/`PropertyListDecoder`) so it wouldn't be completely wasted effort.)

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [June 1, 2021, 11:24am UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/14 "2021-06-01T11:24:07Z")

</div>

+1 for the pitch's proposal. the current behaviour (of supporting Int keys but neither Int16/etc keys nor keys which are raw representible as strings, and serialising some dictionaries as json arrays of key-value pairs ?!) feels broken.

---

<div class="post-metadata">

**Author:** ![Morten\_Bek\_Ditlevsen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/morten_bek_ditlevsen/32/28133_2.png) [@Morten\_Bek\_Ditlevsen](https://forums.swift.org/u/Morten_Bek_Ditlevsen)\
**Post date:** [June 14, 2021, 10:18am UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/15 "2021-06-14T10:18:09Z")

</div>

Hi @Tony_Parker ,

With a most excellent WWDC over, do you have any comments to the feedback for your questions regarding the proposal PR?

Sincerely,  
/morten

---

<div class="post-metadata">

**Author:** ![Tony\_Parker](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tony_parker/32/273_2.png) [@Tony\_Parker](https://forums.swift.org/u/Tony_Parker)\
**Post date:** [July 27, 2021, 10:19pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/16 "2021-07-27T22:19:52Z")

</div>

Hi @Morten_Bek_Ditlevsen ,

Apologies for the late response. I think you've answered my questions on this and it looks reasonable to me.

---

<div class="post-metadata">

**Author:** ![Morten\_Bek\_Ditlevsen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/morten_bek_ditlevsen/32/28133_2.png) [@Morten\_Bek\_Ditlevsen](https://forums.swift.org/u/Morten_Bek_Ditlevsen)\
**Post date:** [August 9, 2021, 6:09pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/17 "2021-08-09T18:09:12Z")

</div>

Hi everybody!  
I’m inviting anyone that participated in this discussion thread to give their two cents on the proposal review thread:

[https://forums.swift.org/t/se-0320-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/50903/6](https://forums.swift.org/t/se-0320-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/50903/6)

Thanks! 🙏

---

<div class="post-metadata">

**Author:** ![Zedd](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/zedd/32/28092_2.png) [@Zedd](https://forums.swift.org/u/Zedd)\
**Post date:** [April 3, 2022, 1:29pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/18 "2022-04-03T13:29:46Z")

</div>

How do I use Int64 as a key? I understand that CodingKeyRepresentable is used for custom types, but is Int64 still not usable as a key?

```swift
let data = Data(
"""
{ "1": "Zedd" }
""".utf8
)

let decoded = try! JSONDecoder().decode([Int64: String].self, from: data)

```

I extended Int64 to comply with CodingKeyRepresentable, but I don't know which codingKey to specify.

```swift
extension Int64: CodingKeyRepresentable {

    public var codingKey: CodingKey {
       // ?? 
    }
}

```

---

<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:** [April 3, 2022, 4:48pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/19 "2022-04-03T16:48:40Z")

</div>

While it's not recommended you conform types you don't own to protocols you don't own, until `Int64` adopts `CodingKeyRepresentable`, you could implement a conformance as follows:

```swift
// Need a key type which can hold any Int value.
struct IntCodingKey: CodingKey {
    let intValue: Int?
    let stringValue: String

    init(intValue: Int) {
        self.intValue = intValue
        self.stringValue = String(describing: intValue)
    }

    init?(stringValue: String) {
        guard let intValue = Int(stringValue) else { return nil }
        self.intValue = intValue
        self.stringValue = stringValue
    }
}

extension Int64: CodingKeyRepresentable {
    public var codingKey: CodingKey {
        guard let intValue = Int(exactly: self) else {
            // Figure out how to handle this!!
        }

        return IntCodingKey(intValue: intValue)
    }

    public init?<T: CodingKey>(codingKey: T) {
        guard let intValue = codingKey.intValue ?? Int(codingKey.stringValue) else {
             return nil
        }

        self.init(exactly: intValue)
    }
}

```

Importantly, you'll need to figure out how to handle converting an `Int64` into an `Int` if you're targeting 32-bit platforms, since a 64-bit `Int64` can't necessarily fit into a 32-bit `Int`. If you can guarantee that you'll only ever target 64-bit platforms or larger, then you won't really need to worry about this (and potentially, a `fatalError` would suffice), but I suspect that the Swift stdlib wouldn't want to implement it that way itself. (Though I could imagine adding conditionally `Int64: CodingKeyRepresentable` to the stdlib based on target architecture — maybe `Int64` _isn't_ `CodingKeyRepresentable` on 32-bit systems.)

---

<div class="post-metadata">

**Author:** ![Stan\_Smida](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/stan_smida/32/10999_2.png) [@Stan\_Smida](https://forums.swift.org/u/Stan_Smida)\
**Post date:** [August 16, 2022, 5:29pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/20 "2022-08-16T17:29:59Z")

</div>

I don't have a strong opinion on the proposed solution yet since I don't have that in-depth knowledge of `Codable` as I feel I should. Regardless of how legitimate were decisions taken when implementing the original solution, we need a `Dictionary` with custom keys `Codable`, in an expected way! It took me several minutes of wtfing to find a root of the problem when I hit this. With the error description that array is expected but dictionary found being not helpful at all, but misleading!  
It should get more attention.

---

<div class="post-metadata">

**Author:** ![ole](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ole/32/18720_2.png) [@ole](https://forums.swift.org/u/ole)\
**Post date:** [August 16, 2022, 5:42pm UTC](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593/21 "2022-08-16T17:42:13Z")

</div>

> [@Stan\_Smida](#):
>
> we need a `Dictionary` with custom keys `Codable`, in an expected way!

This pitch turned into a proposal and was implemented in Swift 5.6: [SE-0320: Allow coding of non String / Int keyed Dictionary into a KeyedContainer](https://github.com/apple/swift-evolution/blob/main/proposals/0320-codingkeyrepresentable.md)

You opt in to the new behavior by conforming your key type to `CodingKeyRepresentable`.

[Next page](https://forums.swift.org/t/pitch-allow-coding-of-non-string-int-keyed-dictionary-into-a-keyedcontainer/44593.md?page=2)
