Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Any in Codable Type

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.

like image 930
PGDev Avatar asked Jan 17 '18 09:01

PGDev


People also ask

Can a class conform to Codable?

If all the properties of a type already conform to Codable , then the type itself can conform to Codable with no extra work – Swift will synthesize the code required to archive and unarchive your type as needed.

What is the use of Codable?

Codable allows you to insert an additional clarifying stage into the process of decoding data into a Swift object. This stage is the “parsed object,” whose properties and keys match up directly to the data, but whose types have been decoded into Swift objects.

How do you implement Codable?

The simplest way to make a type codable is to declare its properties using types that are already Codable . These types include standard library types like String , Int , and Double ; and Foundation types like Date , Data , and URL .


2 Answers

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.

like image 74
Luca Angeletti Avatar answered Oct 28 '22 16:10

Luca Angeletti


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")
like image 27
Scriptable Avatar answered Oct 28 '22 17:10

Scriptable