Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@jaredsinclair
Last active January 22, 2022 00:23
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredsinclair/6a6754dd7a49f2f8f2d4222385bf42a3 to your computer and use it in GitHub Desktop.
Save jaredsinclair/6a6754dd7a49f2f8f2d4222385bf42a3 to your computer and use it in GitHub Desktop.
[Notion]: Automatic Codable synthesis for Enums with Codable associated values.
enum MyEnum: Codable {
case foo
case bar(Int)
case baz(label: String)
case qux(Bool, label: String, Int)
case anotherCase(discriminator: String, anotherCase_1: Int)
}
// Begin synthesized code...
extension MyEnum {
enum Discriminator: String, Codable {
case foo, bar, baz, qux, anotherCase
}
enum CodingKeys: String, CodingKey {
case discriminator
case bar_arg0
case baz_label
case qux_arg0, qux_label, qux_arg2
case anotherCase_discriminator, anotherCase_anotherCase_1
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let discriminator = try container.decode(Discriminator.self, forKey: .discriminator)
switch discriminator {
case .foo:
self = .foo
case .bar:
let arg0 = try container.decode(Int.self, forKey: .bar_arg0)
self = .bar(arg0)
case .baz:
let label = try container.decode(String.self, forKey: .baz_label)
self = .baz(label: label)
case .qux:
let arg0 = try container.decode(Bool.self, forKey: .qux_arg0)
let label = try container.decode(String.self, forKey: .qux_label)
let arg2 = try container.decode(Int.self, forKey: .qux_arg2)
self = .qux(arg0, label: label, arg2)
case .anotherCase:
let discriminator = try container.decode(String.self, forKey: .anotherCase_discriminator)
let anotherCase_1 = try container.decode(Int.self, forKey: .anotherCase_anotherCase_1)
self = .anotherCase(discriminator: discriminator, anotherCase_1: anotherCase_1)
}
}
}
@jaredsinclair
Copy link
Author

jaredsinclair commented Mar 29, 2018

The code synthesis algorithm for Decodable conformance (in broad strokes) would be:

  1. Synthesize a Discriminator enum that is a 1x1 mapping of the target enum's discriminators, but without any associated values, and using a raw String type so it can participate in automatic Codable conformance.
  2. Synthesize a CodingKeys enum used to create a decoding container. CodingKeys should start with a .discriminator case which will be used to decode the Discriminator. For each case of the target enum that has associated values, append a case to CodingKeys for each associated value. If an associated value has a label, use the label as the key. If not, use the index of the associated value's position in the list of values (e.g. 2 for the third position). Prefix the names of all the associated value keys with something (perhaps the relevant discriminator) to prevent naming collisions between a CodingKey and any associated value with an otherwise identical label (see the .anotherCase example above). Using the relevant discriminator as a prefix also prevents naming collisions between synthesized CodingKeys (the alternative would be to enforce uniquing of the synthesized keys, re-using keys across cases as needed).
  3. Synthesize init(from:), first by decoding the Discriminator. Switch on the discriminator, implementing each case so that it attempts to decode only associated values for the relevant case of the target enum. There should already be the required CodingKey cases necessary to perform this decoding. Once all associated values are decoded (if any), decoding is complete.

The corresponding synthesis for Encodable should be obvious given the above description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment