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