I am loading in JSON which has integers that I wish to map to an enum

//Some JSON object{"id": "....","name": "Some Locomotive""livery": 1,"generation": 1// other variables}

I can load this JSON in using:

struct Locomotive: Codable {var id, name: Stringvar generation: Int // var livery: Int -- Replace this with my own enum (below)var livery: Livery?private enum CodingKeys: CodingKey {case id, name, generationcase livery = "livery" // complains of raw value issue}}

Currently both the generaiton and livery are just Integers; but to make coding easier for me I wish to use map the livery Integer to an enum; so rather than remembering 1 = green, etc; I can just say .green

But I am have trouble mapping the key livery to my enum.

Enum case cannot have a raw value if the enum does not have a raw type

But I'm sure it does; I've defined the raw value in the enum as private though;

enum Livery : Codable {case green, red, blue, yellow}extension Livery {private enum RawValue: Int, Codable, CaseIterable {case green = 1, red, yellow, blue}private enum CodingKeys: Int, CodingKey {case green, red, blue, yellow}init(from decoder: Decoder) throws {let container = try decoder.container(keyedBy: CodingKeys.self)let key = container.allKeys.firstswitch key {case .green:self = .greencase .red:self = .redcase .yellow:self = .yellowcase .blue:self = .bluedefault:throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: container.codingPath,debugDescription: "Error -- Unabled to decode."))}}func encode(to encoder: Encoder) throws {var container = encoder.singleValueContainer()switch self {case .green:try container.encode(RawValue.green)case .red:try container.encode(RawValue.red)case .yellow:try container.encode(RawValue.yellow)case .blue:try container.encode(RawValue.blue)}}}

The above enum decodes the raw value and encodes it.

However, I am unable to map the Livery in the parent struct to this enum and I'm wondering how I can do this?

...

I think I have to implement init(from decoder: Decoder) and encode(to encoder: Encoder) to this structure too -- especially if I want to save my data to JSON in the future; but I am not sure.

Thus, my query is - how do you map an Integer provided by the JSON to a custom enum for both saving (encoding) and loading (decoding).

With thanks

2

Best Answer


You can simplify your code quite a lot, all you need is the below code. By saying that your enum is of type Int swift can synthesise the correct decoding code

struct Locomotive: Codable {var id, name: Stringvar generation: Intvar livery: Livery?}enum Livery: Int, Codable {case green = 1, red, blue, yellow}

The issue is that you declared your Locomotive.CodingKeys enum incorrectly. All CodingKey compliant enums need to have a rawValue, but you declared CodingKeys with no rawValue. Giving it a String rawValue solves your problem.

struct Locomotive: Codable {var id, name: Stringvar generation: Int// var livery: Int -- Replace this with my own enum (below)var livery: Livery?private enum CodingKeys: String, CodingKey {case id, name, generation, livery}}