Тип не соответствует протоколу «Кодируемый» - PullRequest
7 голосов
/ 11 января 2020

У меня есть класс с именем Event, который я хочу сделать его 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)
        }
    }
}

, но я получаю эту ошибку:

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

Я реализовал оба init(from decoder: Decoder) и encode(from encoder: Encoder). так что я тут делаю не так?

Мне нужно свойство data с типом [String:Any], потому что мои пользователи должны хранить с ним некоторую json -подобную информацию.

1 Ответ

2 голосов
/ 11 января 2020

Вот как вы можете использовать ЛЮБОЙ в 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
...