I ran into the same problem when trying to decode / encode an βeditedβ field in a Reddit Listing JSON response. I created a structure that represents the dynamic type that may exist for a given key. A key can have either a logical or an integer.
{ "edited": false } { "edited": 123456 }
If you only need to decrypt, just do init (from :). If you need to go in both directions, you need to implement the encode (to :) function.
struct Edited: Codable { let isEdited: Bool let editedTime: Int // Where we determine what type the value is init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() // Check for a boolean do { isEdited = try container.decode(Bool.self) editedTime = 0 } catch { // Check for an integer editedTime = try container.decode(Int.self) isEdited = true } } // We need to go back to a dynamic type, so based on the data we have stored, encode to the proper type func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try isEdited ? container.encode(editedTime) : container.encode(false) } }
Inside the Codable class, I use my structure.
struct Listing: Codable { let edited: Edited }
Edit: More specific solution for your scenario.
I recommend using the CodingKey protocol and enumeration to store all properties during decoding. When you create something that matches Codable, the compiler will create a private CodingKeys enumeration for you. This allows you to decide what to do based on the key properties of the JSON object.
Just for example, this is JSON I decryption:
{"type": "1.234"} {"type": 1.234}
If you want to convert from a string to a double, because you only need a double value, just decrypt the string and then create a double from it. (This is what Itai Ferber does, you will also have to decode all the properties using try decoder.decode (type: forKey :))
struct JSONObjectCasted: Codable { let type: Double? init(from decoder: Decoder) throws {
If you need to save a type along with a value, you can use an enumeration that matches Codable instead of a structure. Then you can simply use the switch statement with the type property of JSONObjectCustomEnum and perform actions based on this case.
struct JSONObjectCustomEnum: Codable { let type: DynamicJSONProperty } // Where I can represent all the types that the JSON property can be. enum DynamicJSONProperty: Codable { case double(Double) case string(String) init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() // Decode the double do { let doubleVal = try container.decode(Double.self) self = .double(doubleVal) } catch DecodingError.typeMismatch { // Decode the string let stringVal = try container.decode(String.self) self = .string(stringVal) } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .double(let value): try container.encode(value) case .string(let value): try container.encode(value) } } }