Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type does not conform to protocol 'Encodable'

Tags:

ios

swift

codable

I have a class named Event that I want to make it Codable:

class Event: Codable {
    let name: String
    let action: String
    let data: [String: Any]?

    enum CodingKeys: String, CodingKey {
        case name
        case action
        case data
    }

    init(name: String, action: String, data: [String: Any]?) {
        self.name = name
        self.action = action
        self.data = data
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try values.decode(String.self, forKey: .name)
        self.action = try values.decode(String.self, forKey: .action)
        let eventDataAsJSONString = try values.decode(String.self, forKey: .data)
        if let eventDataAsData = eventDataAsJSONString.data(using: .utf8) {
            self.data = try? JSONSerialization.jsonObject(with: eventDataAsData, options: []) as? [String: Any]
        } else {
            self.data = nil
        }
    }

    func encode(from encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.name, forKey: .name)
        try container.encode(self.action, forKey: .action)
        if let data = self.data {
            let eventDataAsData = try! JSONSerialization.data(withJSONObject: data, options: [])
            let eventDataAsJSONString = String(data: eventDataAsData, encoding: .utf8)
            try container.encode(eventDataAsJSONString, forKey: .data)
        } else {
            try container.encodeNil(forKey: .data)
        }
    }
}

but I'm getting this error:

Type 'Event' does not conform to protocol 'Encodable'

I implemented both init(from decoder: Decoder) and encode(from encoder: Encoder). so what I'm doing wrong here?

I need data property with [String:Any] type, because my users need to store some json-like information with it.

like image 411
Jafar Khoshtabiat Avatar asked Jan 11 '20 13:01

Jafar Khoshtabiat


People also ask

Can a protocol conform to Codable?

Codable is Encodable , but the compiler says it does not conform to it. This may sound strange, but it's because we are using Codable as a type, and the type Codable (not the protocol definition of Codable ) does not conform to Encodable .

How do you use any in Codable Swift?

I know that Any is not Codable . What I need to know is how can I make it work. Related: Swift structures handling multiple tapes 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.

What is swift Codable?

Codable is the combined protocol of Swift's Decodable and Encodable protocols. Together they provide standard methods of decoding data for custom types and encoding data to be saved or transferred.


1 Answers

This is how you can use ANY in Codable

class Event<T: Codable>: Codable {
    let name: String
    let action: String
    let data: [String: T]?

    enum CodingKeys: String, CodingKey {
        case name
        case action
        case data
    }

    init(name: String, action: String, data: [String: T]?) {
        self.name = name
        self.action = action
        self.data = data
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try values.decode(String.self, forKey: .name)
        self.action = try values.decode(String.self, forKey: .action)
        let eventDataAsJSONString = try values.decode(String.self, forKey: .data)
        if let eventDataAsData = eventDataAsJSONString.data(using: .utf8) {
            self.data = try? JSONSerialization.jsonObject(with: eventDataAsData, options: []) as? [String: T]
        } else {
            self.data = nil
        }
    }

    func encode(from encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.name, forKey: .name)
        try container.encode(self.action, forKey: .action)
        if let data = self.data {
            let eventDataAsData = try! JSONSerialization.data(withJSONObject: data, options: [])
            let eventDataAsJSONString = String(data: eventDataAsData, encoding: .utf8)
            try container.encode(eventDataAsJSONString, forKey: .data)
        } else {
            try container.encodeNil(forKey: .data)
        }
    }
}


     let event = Event<String>(name: "name", action: "action", data: ["String" : "String"]) // Replace <String> with the type u want and pass that in data
like image 67
Nitesh Avatar answered Oct 23 '22 12:10

Nitesh