1

I'm struggling to write a single function that encodes the following struct:

struct Lookup: Encodable {
    var id: Int
    var name: String
    
    enum StateCodingKeys: String, CodingKey {
        case id = "stateId"
        case name = "stateName"
    }
    
    enum CityCodingKeys: String, CodingKey {
        case id = "cityId"
        case name = "cityName"
    }
    
    func encode(to encoder: Encoder, type: StateCodingKeys.Type) throws {
        var container = encoder.container(keyedBy: type)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
      }
}

the custom encode function here takes StateCodingKeys.Type as a parameter, but I can't find a way to let this function accept any CodingKey type, like the CityCodingKeys enum, is there a way to do that ?

JAHelia
  • 6,934
  • 17
  • 74
  • 134
  • 1
    Are you implementing a separate `encode(to:)` method that is not shown? Because the generated `Encodable` conformance is not going to call your `encode(to:type:)` method. – Sweeper Oct 17 '22 at 15:12
  • No, I will just call: `lookUpObj.encode(to:type:)` when I encode the object `lookupObj` – JAHelia Oct 17 '22 at 15:15
  • 2
    Well, that works too :) In that case you don't actually need to conform to `Encodable`, and I would argue that you *should* not. – Sweeper Oct 17 '22 at 15:18

1 Answers1

3

You can create a common protocol for both of your enums, add the enum cases you need as static vars, and conform the enums to the protocol.

protocol LookupCodingKey: CodingKey {
    static var id: Self { get }
    static var name: Self { get }
}
enum StateCodingKeys: String, LookupCodingKey {
    case id = "stateId"
    case name = "stateName"
}

enum CityCodingKeys: String, LookupCodingKey {
    case id = "cityId"
    case name = "cityName"
}

Then you can add the protocol as a generic constraint:

func encode<CodingKeyType: LookupCodingKey>(to encoder: Encoder, type: CodingKeyType.Type) throws {
    var container = encoder.container(keyedBy: type)
    try container.encode(id, forKey: .id)
    try container.encode(name, forKey: .name)
}

Side note:

If you just want to call encode(to:type:) directly to encode a Lookup, I would suggest that you do not conform to Encodable, since Lookup would have a generated encode(to:) method, that does not call your encode(to:type:).

When you accidentally pass Lookup to something that expects an Encodable, and that something encodes it using encode(to:), it will have the unexpected keys id and name.

I haven't tried, but you might be able to conform to EncodableWithConfiguration instead, with the configuration being the type of the coding key.

Sweeper
  • 213,210
  • 22
  • 193
  • 313