55

I'm currently working with Codable types in my project and facing an issue.

struct Person: Codable
{
    var id: Any
}

id in the above code could be either a String or an Int. This is the reason id is of type Any.

I know that Any is not Codable.

What I need to know is how can I make it work.

halfer
  • 19,824
  • 17
  • 99
  • 186
PGDev
  • 23,751
  • 6
  • 34
  • 88
  • 4
    Related: [Swift structures handling multiple tapes for a single property](https://stackoverflow.com/questions/46759044/swift-structures-handling-multiple-types-for-a-single-property). In summary: you shouldn’t use ‘Any’, but have 2 optional properties (one of type ‘String’ and one ‘Int’ in your case) and try decoding the JSON value as both. Moreover, your case is actually quite simple, since ‘Int’ can always be converted to ‘String’. – Dávid Pásztor Jan 17 '18 at 09:23
  • The linked answer also answers this question, even if you use `Any`. You shouldn't use `Any`, you should use an enum, but the approach still works exactly the same way for `Any`; just manually decode from the container and see if it works. If not, move on to the next type. – Rob Napier Jan 22 '18 at 16:57
  • 1
    This sort of thing has been answered many times. The Int-Or-String problem, for example, is neatly solved here: https://stackoverflow.com/a/47215561/341994 See for example also https://stackoverflow.com/questions/46392046/how-do-you-design-a-codable-json-field-which-can-be-either-an-empty-string-or-an as well as e.g. https://stackoverflow.com/questions/44603248/how-to-decode-a-property-with-type-of-json-dictionary-in-swift-4-decodable-proto To open a bounty without searching adequately is kind of a waste. – matt Jan 22 '18 at 18:42
  • @matt: can this be closed as a dup? – halfer May 20 '18 at 21:29
  • @halfer I don't think it's a dup. None of the previously asked questions could answer the queries we have here. Also, none of questions could be answer in such numerous ways . – PGDev May 21 '18 at 05:50

13 Answers13

65

Quantum Value

First of all you can define a type that can be decoded both from a String and Int value. Here it is.

enum QuantumValue: Decodable {
    
    case int(Int), string(String)
    
    init(from decoder: Decoder) throws {
        if let int = try? decoder.singleValueContainer().decode(Int.self) {
            self = .int(int)
            return
        }
        
        if let string = try? decoder.singleValueContainer().decode(String.self) {
            self = .string(string)
            return
        }
        
        throw QuantumError.missingValue
    }
    
    enum QuantumError:Error {
        case missingValue
    }
}

Person

Now you can define your struct like this

struct Person: Decodable {
    let id: QuantumValue
}

That's it. Let's test it!

JSON 1: id is String

let data = """
{
"id": "123"
}
""".data(using: String.Encoding.utf8)!

if let person = try? JSONDecoder().decode(Person.self, from: data) {
    print(person)
}

JSON 2: id is Int

let data = """
{
"id": 123
}
""".data(using: String.Encoding.utf8)!

if let person = try? JSONDecoder().decode(Person.self, from: data) {
    print(person)
}

UPDATE 1 Comparing values

This new paragraph should answer the questions from the comments.

If you want to compare a quantum value to an Int you must keep in mind that a quantum value could contain an Int or a String.

So the question is: what does it mean comparing a String and an Int?

If you are just looking for a way of converting a quantum value into an Int then you can simply add this extension

extension QuantumValue {
    
    var intValue: Int? {
        switch self {
        case .int(let value): return value
        case .string(let value): return Int(value)
        }
    }
}

Now you can write

let quantumValue: QuantumValue: ...
quantumValue.intValue == 123

UPDATE 2

This part to answer the comment left by @Abrcd18.

You can add this computed property to the Person struct.

var idAsString: String {
    switch id {
    case .string(let string): return string
    case .int(let int): return String(int)
    }
}

And now to populate the label just write

label.text = person.idAsString

Hope it helps.

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
  • 1
    Firstly, let me say that, this solution is pretty smart and works super for the asked question. Something you could add to your answer is later on usage of the property: `if case let QuantumValue.string(s) = person { print("id is: \(s)") }` . If we go a little bit further, it is horrible to do something like this from logic perspective of the code. On my opinion, the better solution is to communicate a change in the response object so it is consistent with only a single type. – dvp.petrov May 18 '18 at 12:32
  • i have a doubt that how can we compare (person.id == 123) i am not able to compare is there any way to convert QuantumValue to int? @Luca Angeletti – Dhanunjay Kumar Jun 04 '19 at 13:37
  • @DhanunjayKumar did you figure this out? – King Jun 21 '19 at 14:45
  • @King no i am not able to compare quantumValue to int or double . so i am converting int to double while parsing. any clue how to compare? – Dhanunjay Kumar Jun 21 '19 at 18:25
  • @DhanunjayKumar Please check the new paragraph in my answer – Luca Angeletti Jun 22 '19 at 11:51
  • I had the same issue with a backend with an inconsistency in the data type, and this is the answer that helps me, also nice naming. – BlaShadow Sep 10 '20 at 14:58
  • Hello! I followed your answer and faced the following problem. Text on label is printed as "int(1919)" and "double(3939.2)". Could you please say what should I do to remove "int" and "double" before numbers? – Abrcd18 Oct 12 '20 at 17:08
  • 1
    Hey @Abrcd18, have a look at the update I added to my answer. – Luca Angeletti Oct 17 '20 at 07:54
  • @LucaAngeletti How to update the quantum value? I want to send same model data to server with updated value..assume that "flag" (by default 0 from server ) is Quantum in model(let flag : QuantumValue?) now i want to change the value to 1 – karthikeyan Mar 15 '22 at 07:10
  • @karthikeyan `YourDataModel(flag: .int(1))` – Luca Angeletti Mar 16 '22 at 09:40
  • @LucaAngeletti Thanks for your comment..i fixed it already, someone might get useful...Thank you so much – karthikeyan Mar 16 '22 at 11:45
27

Codable needs to know the type to cast to.

Firstly I would try to address the issue of not knowing the type, see if you can fix that and make it simpler.

Otherwise the only way I can think of solving your issue currently is to use generics like below.

struct Person<T> {
    var id: T
    var name: String
}

let person1 = Person<Int>(id: 1, name: "John")
let person2 = Person<String>(id: "two", name: "Steve")
Scriptable
  • 19,402
  • 5
  • 56
  • 72
  • 1
    In your approach to use generics, I must still know the data type of `id` that I am getting from `JSON`. – PGDev Jan 17 '18 at 09:18
  • 4
    yes, hence the first sentence of my answer. If you do not KNOW the type you cannot use codable. you will need to try conditional unwrapping. Codable must know the type. – Scriptable Jan 17 '18 at 09:20
18

I solved this issue defining a new Decodable Struct called AnyDecodable, so instead of Any I use AnyDecodable. It works perfectly also with nested types.

Try this in a playground:

var json = """
{
  "id": 12345,
  "name": "Giuseppe",
  "last_name": "Lanza",
  "age": 31,
  "happy": true,
  "rate": 1.5,
  "classes": ["maths", "phisics"],
  "dogs": [
    {
      "name": "Gala",
      "age": 1
    }, {
      "name": "Aria",
      "age": 3
    }
  ]
}
"""

public struct AnyDecodable: Decodable {
  public var value: Any

  private struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  public init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyDecodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyDecodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

let stud = try! JSONDecoder().decode(AnyDecodable.self, from: jsonData).value as! [String: Any]
print(stud)

You could extend my struct to be AnyCodable if you are interested also in the Encoding part.

Edit: I actually did it.

Here is AnyCodable

struct AnyCodable: Decodable {
  var value: Any

  struct CodingKeys: CodingKey {
    var stringValue: String
    var intValue: Int?
    init?(intValue: Int) {
      self.stringValue = "\(intValue)"
      self.intValue = intValue
    }
    init?(stringValue: String) { self.stringValue = stringValue }
  }

  init(value: Any) {
    self.value = value
  }

  init(from decoder: Decoder) throws {
    if let container = try? decoder.container(keyedBy: CodingKeys.self) {
      var result = [String: Any]()
      try container.allKeys.forEach { (key) throws in
        result[key.stringValue] = try container.decode(AnyCodable.self, forKey: key).value
      }
      value = result
    } else if var container = try? decoder.unkeyedContainer() {
      var result = [Any]()
      while !container.isAtEnd {
        result.append(try container.decode(AnyCodable.self).value)
      }
      value = result
    } else if let container = try? decoder.singleValueContainer() {
      if let intVal = try? container.decode(Int.self) {
        value = intVal
      } else if let doubleVal = try? container.decode(Double.self) {
        value = doubleVal
      } else if let boolVal = try? container.decode(Bool.self) {
        value = boolVal
      } else if let stringVal = try? container.decode(String.self) {
        value = stringVal
      } else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "the container contains nothing serialisable")
      }
    } else {
      throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Could not serialise"))
    }
  }
}

extension AnyCodable: Encodable {
  func encode(to encoder: Encoder) throws {
    if let array = value as? [Any] {
      var container = encoder.unkeyedContainer()
      for value in array {
        let decodable = AnyCodable(value: value)
        try container.encode(decodable)
      }
    } else if let dictionary = value as? [String: Any] {
      var container = encoder.container(keyedBy: CodingKeys.self)
      for (key, value) in dictionary {
        let codingKey = CodingKeys(stringValue: key)!
        let decodable = AnyCodable(value: value)
        try container.encode(decodable, forKey: codingKey)
      }
    } else {
      var container = encoder.singleValueContainer()
      if let intVal = value as? Int {
        try container.encode(intVal)
      } else if let doubleVal = value as? Double {
        try container.encode(doubleVal)
      } else if let boolVal = value as? Bool {
        try container.encode(boolVal)
      } else if let stringVal = value as? String {
        try container.encode(stringVal)
      } else {
        throw EncodingError.invalidValue(value, EncodingError.Context.init(codingPath: [], debugDescription: "The value is not encodable"))
      }

    }
  }
}

You can test it With the previous json in this way in a playground:

let stud = try! JSONDecoder().decode(AnyCodable.self, from: jsonData)
print(stud.value as! [String: Any])

let backToJson = try! JSONEncoder().encode(stud)
let jsonString = String(bytes: backToJson, encoding: .utf8)!

print(jsonString)
Giuseppe Lanza
  • 3,519
  • 1
  • 18
  • 40
  • Thanks Giuseppe! This is the most practical answer even though it's not truly 'AnyCodable', because Any can only be primitive types here (not other custom Codable types), but it should be good enough for most of the use cases... Btw, why don't you include other types like Date, Data, URL which are also natively supported? – Natural Lam Jan 26 '18 at 10:03
  • Because in Json they are strings. To know they represent date data and URL we would need insights that with "any" we don't have. :/ – Giuseppe Lanza Jan 26 '18 at 10:11
  • I see.. For my use case as I use both encode & decode as a pair from the app (i.e. the backend data always saved from the app as well), I think it should just work with the default date/data/url scheme that Swift use – Natural Lam Jan 26 '18 at 18:53
  • This works perfectly. Nice work. Ironically though, in the end, I think doing this is pointless. When you end up wanting to use the parsed data somewhere, you need to know where that data is within the structure and what type it is. So if you are going to go through that optional chain and casting, you might as well have defined it in standard Codable objects in the first place. Just don't parse the data you don't know about, which is easily achieved in Codable by omitting keys. – n8tr Jun 22 '18 at 15:12
  • This is very interesting, @GiuseppeLanza. But is there a way to improve decoding by automatically transform [Any] into [], if we see a homogenous array? – DrMickeyLauer Apr 23 '21 at 06:37
  • @GiuseppeLanza this is great, thank you for that piece of code but the encode() does not work for Codable structs. – barola_mes Oct 06 '21 at 09:18
10

If your problem is that it's uncertain the type of id as it might be either a string or an integer value, I can suggest you this blog post: http://agostini.tech/2017/11/12/swift-4-codable-in-real-life-part-2/

Basically I defined a new Decodable type

public struct UncertainValue<T: Decodable, U: Decodable>: Decodable {
    public var tValue: T?
    public var uValue: U?

    public var value: Any? {
        return tValue ?? uValue
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        tValue = try? container.decode(T.self)
        uValue = try? container.decode(U.self)
        if tValue == nil && uValue == nil {
            //Type mismatch
            throw DecodingError.typeMismatch(type(of: self), DecodingError.Context(codingPath: [], debugDescription: "The value is not of type \(T.self) and not even \(U.self)"))
        }

    }
}

From now on, your Person object would be

struct Person: Decodable {
    var id: UncertainValue<Int, String>
}

you will be able to access your id using id.value

Giuseppe Lanza
  • 3,519
  • 1
  • 18
  • 40
6

Simply you can use AnyCodable type from Matt Thompson's cool library AnyCodable.

Eg:

import AnyCodable

struct Person: Codable
{
    var id: AnyCodable
}
Johnykutty
  • 12,091
  • 13
  • 59
  • 100
  • Interesting, but let's say id is a String, how can I convert / parse it to a String, since `id as String` won't work? – matteoh Jul 18 '21 at 10:19
4

To make key as Any, I like all above answers. But when you are not sure which data type your server guy will send then you use Quantum class (as above), But Quantum type is little difficult to use or manage. So here is my solution to make your decodable class key as a Any data type (or "id" for obj-c lovers)

   class StatusResp:Decodable{
    var success:Id? // Here i am not sure which datatype my server guy will send
}
enum Id: Decodable {

    case int(Int), double(Double), string(String) // Add more cases if you want

    init(from decoder: Decoder) throws {

        //Check each case
        if let dbl = try? decoder.singleValueContainer().decode(Double.self),dbl.truncatingRemainder(dividingBy: 1) != 0  { // It is double not a int value
            self = .double(dbl)
            return
        }

        if let int = try? decoder.singleValueContainer().decode(Int.self) {
            self = .int(int)
            return
        }
        if let string = try? decoder.singleValueContainer().decode(String.self) {
            self = .string(string)
            return
        }
        throw IdError.missingValue
    }

    enum IdError:Error { // If no case matched
        case missingValue
    }

    var any:Any{
        get{
            switch self {
            case .double(let value):
                return value
            case .int(let value):
                return value
            case .string(let value):
                return value
            }
        }
    }
}

Usage :

let json = "{\"success\":\"hii\"}".data(using: .utf8) // response will be String
        //let json = "{\"success\":50.55}".data(using: .utf8)  //response will be Double
        //let json = "{\"success\":50}".data(using: .utf8) //response will be Int
        let decoded = try? JSONDecoder().decode(StatusResp.self, from: json!)
        print(decoded?.success) // It will print Any

        if let doubleValue = decoded?.success as? Double {

        }else if let doubleValue = decoded?.success as? Int {

        }else if let doubleValue = decoded?.success as? String {

        }
indrajit
  • 303
  • 1
  • 14
  • This line: 'if let string' would be better if it would be 'else if let'. – J. Doe Aug 15 '18 at 07:45
  • Writing this part inside the enum solved my issue var any:Any{ get{ switch self { case .double(let value): return value case .int(let value): return value case .string(let value): return value } } } – Albi Apr 27 '21 at 07:27
3

You can replace Any with an enum accepting an Int or a String:

enum Id: Codable {
    case numeric(value: Int)
    case named(name: String)
}

struct Person: Codable
{
    var id: Id
}

Then the compiler will complain about the fact that Id does not conform to Decodable. Because Id has associated values you need to implement this yourself. Read https://littlebitesofcocoa.com/318-codable-enums for an example of how to do this.

Clafou
  • 15,250
  • 7
  • 58
  • 89
3

Thanks to Luka Angeletti's answer (https://stackoverflow.com/a/48388443/7057338) i've changed enum to struct so we can use it more easily

struct QuantumValue: Codable {

    public var string: String?
    public var integer: Int?

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let int = try? container.decode(Int.self) {
            self.integer = int
            return
        }
        if let string = try? container.decode(String.self) {
            self.string = string
            return
        }
        throw QuantumError.missingValue
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(string)
        try container.encode(integer)
    }

    enum QuantumError: Error {
         case missingValue
    }

    func value() -> Any? {
        if let s = string {
            return s
        }
        if let i = integer {
            return i
        }
        return nil
    }
}
Mr.Zee
  • 125
  • 1
  • 9
2

First of all, as you can read in other answers and comments, using Any for this is not good design. If possible, give it a second thought.

That said, if you want to stick to it for your own reasons, you should write your own encoding/decoding and adopt some kind of convention in the serialized JSON.

The code below implements it by encoding id always as string and decoding to Int or String depending on the found value.

import Foundation

struct Person: Codable {
    var id: Any

    init(id: Any) {
        self.id = id
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: Keys.self)
        if let idstr = try container.decodeIfPresent(String.self, forKey: .id) {
            if let idnum = Int(idstr) {
                id = idnum
            }
            else {
                id = idstr
            }
            return
        }
        fatalError()
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: Keys.self)
        try container.encode(String(describing: id), forKey: .id)
    }

    enum Keys: String, CodingKey {
        case id
    }
}

extension Person: CustomStringConvertible {
    var description: String { return "<Person id:\(id)>" }
}

Examples

Encode object with numeric id:

var p1 = Person(id: 1)
print(String(data: try JSONEncoder().encode(p1), 
      encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"1"}

Encode object with string id:

var p2 = Person(id: "root")
print(String(data: try JSONEncoder().encode(p2), 
      encoding: String.Encoding.utf8) ?? "/* ERROR */")
// {"id":"root"}

Decode to numeric id:

print(try JSONDecoder().decode(Person.self, 
      from: "{\"id\": \"2\"}".data(using: String.Encoding.utf8)!))
// <Person id:2>

Decode to string id:

print(try JSONDecoder().decode(Person.self, 
      from: "{\"id\": \"admin\"}".data(using: String.Encoding.utf8)!))
// <Person id:admin>

An alternative implementation would be encoding to Int or String and wrap the decoding attempts in a do...catch.

In the encoding part:

    if let idstr = id as? String {
        try container.encode(idstr, forKey: .id)
    }
    else if let idnum = id as? Int {
        try container.encode(idnum, forKey: .id)
    }

And then decode to the right type in multiple attempts:

do {
    if let idstr = try container.decodeIfPresent(String.self, forKey: .id) {
        id = idstr
        id_decoded = true
    }
}
catch {
    /* pass */
}

if !id_decoded {
    do {
        if let idnum = try container.decodeIfPresent(Int.self, forKey: .id) {
            id = idnum
        }
    }
    catch {
        /* pass */
    }
}

It's uglier in my opinion.

Depending on the control you have over the server serialization you can use either of them or write something else adapted to the actual serialization.

djromero
  • 19,551
  • 4
  • 71
  • 68
2

Here your id can be any Codable type:

Swift 4.2

struct Person<T: Codable>: Codable {

    var id: T
    var name: String?
}

let p1 = Person(id: 1, name: "Bill")
let p2 = Person(id: "one", name: "John")
Mad Man
  • 29
  • 4
  • Code-only answers are discouraged. Please click on [edit] and add some words summarising how your code addresses the question, or perhaps explain how your answer differs from the previous answer/answers. Thanks – Nick Dec 24 '18 at 02:32
  • how is this different from the already accepted answer? You literally copy pasted that. This is wrong on so many levels... – Dani Pralea Aug 18 '20 at 18:57
  • "wrong on so many levels" what you mean? It's different. – Mad Man Oct 22 '20 at 04:28
2

Swift 5

This is an update about the best answer (IMHO) from Luca Angeletti, so to perform your request:

enum PersonAny: Codable {
    case int(Int), string(String) // Insert here the different type to encode/decode
    init(from decoder: Decoder) throws {
        if let int = try? decoder.singleValueContainer().decode(Int.self) {
            self = .int(int)
            return
        }
        if let string = try? decoder.singleValueContainer().decode(String.self) {
            self = .string(string)
            return
        }
        throw AnyError.missingValue
    }
    enum AnyError:Error {
        case missingValue
    }
}

// Your declaration
struct Person: Codable
{
    var id: PersonAny
}
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
0

There is a corner case which is not covered by Luca Angeletti's solution.

For instance, if Cordinate's type is Double or [Double], Angeletti's solution will cause an error: "Expected to decode Double but found an array instead"

In this case, you have to use nested enum instead in Cordinate.

enum Cordinate: Decodable {
    case double(Double), array([Cordinate])

    init(from decoder: Decoder) throws {
        if let double = try? decoder.singleValueContainer().decode(Double.self) {
            self = .double(double)
            return
        }

        if let array = try? decoder.singleValueContainer().decode([Cordinate].self) {
            self = .array(array)
            return
        }

        throw CordinateError.missingValue
    }

    enum CordinateError: Error {
        case missingValue
    }
}

struct Geometry : Decodable {
    let date : String?
    let type : String?
    let coordinates : [Cordinate]?

    enum CodingKeys: String, CodingKey {

        case date = "date"
        case type = "type"
        case coordinates = "coordinates"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        date = try values.decodeIfPresent(String.self, forKey: .date)
        type = try values.decodeIfPresent(String.self, forKey: .type)
        coordinates = try values.decodeIfPresent([Cordinate].self, forKey: .coordinates)
    }
}
webcpu
  • 3,174
  • 2
  • 24
  • 17
0

I took reference from Codable Protocol

/**
 Usage:
 ======
  struct PersonInfo: Codable {
   var userName: String?
   var EmpId : AnyCodable?
 }
*/

enum AnyCodable {
case string(value: String)
case int(value: Int)
case data(value: Data)
case double(value: Double)

func toString() -> String? {
    switch self {
    case .string(value: let value):
        return value
    case .int(value: let value):
        return "\(value)"
    case .data(value: let value):
        return String(decoding: value, as: UTF8.self)
    case .double(value: let value):
        let formatter = NumberFormatter()
        formatter.maximumFractionDigits = 0 // Sets the number of decimal places to 2
        let myString = formatter.string(from: NSNumber(value: value))
        return myString
    }
}

enum AnyCodableError:Error {
    case missingValue
 }
}

extension AnyCodable: Codable {

enum CodingKeys: String, CodingKey {
    case string, int, data, double
}

init(from decoder: Decoder) throws {
    if let int = try? decoder.singleValueContainer().decode(Int.self) {
        self = .int(value: int)
        return
    }
    
    if let string = try? decoder.singleValueContainer().decode(String.self) {
        self = .string(value: string)
        return
    }
    
    if let data = try? decoder.singleValueContainer().decode(Data.self) {
        self = .data(value: data)
        return
    }
    
    if let double = try? decoder.singleValueContainer().decode(Double.self) {
        self = .double(value: double)
        return
    }
    
    throw AnyCodableError.missingValue
}

func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    switch self {
    case .string(let value):
        try container.encode(value, forKey: .string)
    case .int(let value):
        try container.encode(value, forKey: .int)
    case .data(let value):
        try container.encode(value, forKey: .data)
    case .double(let value):
        try container.encode(value, forKey: .double)
    }
 }
}
Mannam Brahmam
  • 2,225
  • 2
  • 24
  • 36