Не удается преобразовать значение типа ‘Codable’ (он же ‘Decodable

#json #swift #xcode #generics #swift5

#json #swift #xcode #общие #swift5

Вопрос:

Я работаю над небольшой структурой, которая обрабатывает синтаксический анализ JSON. До сих пор все работало, за исключением того, что я хочу передать пользовательскую структуру в качестве структуры для использования при декодировании, JSONDecoder().decode(type.self, from: data) но это выдает следующую ошибку:

Не удается преобразовать значение типа ‘Codable’ (он же ‘Decodable amp; Encodable’) в ожидаемый тип аргумента ‘T.Тип’

 private func parseJson(data: Data, type: Codable) -> Codable? {
    do {
        let decoded = try JSONDecoder().decode(type.self, from: data)
        
        return decoded
    } catch {
        print("JSON decode error: (error.localizedDescription)")
    }
    
    return nil
}
  

Есть ли способ, которым я могу передать структуру в этот метод для использования в качестве типа для decode() функции? Если я напрямую задаю тип, который я пытаюсь передать в функцию в функции decode (), код работает как ожидалось, он выдает ошибку только при попытке его передать.

Ответ №1:

Что вам нужно, так это универсальный метод:

 private func parseJson<T: Decodable>(data: Data, type: T.Type) -> T? {
    do {
        return try JSONDecoder().decode(type.self, from: data)
    } catch {
        print("JSON decode error:", error)
        return nil
    }
}
  

Вы также можете опустить тип и явно задать тип результирующего объекта:

 private func parseJson<T: Decodable>(data: Data) -> T? {
    do {
        return try JSONDecoder().decode(T.self, from: data)
    } catch {
        print("JSON decode error:", error)
        return nil
    }
}
  

Тестирование игровой площадки:

 struct User: Codable {
    let id: Int
    let name: String
}
let user: User = .init(id: 2, name: "abc")
let userData = try! JSONEncoder().encode(user)

let decodedUser: User = parseJson(data: userData)!
decodedUser.name  // "abc"
  

Примечание: я возвращаю необязательные типы, но вы обязательно должны заставить свои методы выдавать и возвращать необязательные, как вы можете видеть ниже, где я расширяю данные:

 extension Data {
    func decodedObject<T: Decodable>() throws -> T {
        try JSONDecoder().decode(T.self, from: self)
    }
}
  

 do {
    let decodedUser: User = try userData.decodedObject()
    print(decodedUser.name)  // "abc"
} catch {
    print(error)
}
  

Комментарии:

1. Кажется, это работает, но я не совсем понимаю, как, не могли бы вы объяснить это немного подробнее?

2. Codable это не тип, это протокол. T является универсальным типом, который должен соответствовать Decodable

3. Второе решение для меня не работает. Он выдает Cannot explicitly specialize a generic function ошибку.