无法将符合协议的元素映射到通用元素 [英] Cannot map protocol compliant elements to generic elements
问题描述
如下所示,我下载了包含异类对象的结构数组,这些结构被解码为包含嵌套对象的枚举.
As you can see below, I downloaded an array of structures containing heterogeneous objects that were decoded into enums containing nested objects.
我现在想将所述对象放入通用的Model结构中,但是编译器不允许这样做-错误在下面的代码注释中进行了描述.我对使用Swift进行编程相对较新,希望您的帮助.
I would now like to put said objects into a generic Model structure, but the compiler won't allow this - the error is described below in the code comment. I am relatively new to programming in Swift, I would appreciate your help.
import Foundation
let jsonString = """
{
"data":[
{
"type":"league",
"info":{
"name":"NBA",
"sport":"Basketball",
"website":"https://nba.com/"
}
},
{
"type":"player",
"info":{
"name":"Kawhi Leonard",
"position":"Small Forward",
"picture":"https://i.ibb.co/b5sGk6L/40a233a203be2a30e6d50501a73d3a0a8ccc131fv2-128.jpg"
}
},
{
"type":"team",
"info":{
"name":"Los Angeles Clippers",
"state":"California",
"logo":"https://logos-download.com/wp-content/uploads/2016/04/LA_Clippers_logo_logotype_emblem.png"
}
}
]
}
"""
struct Response: Decodable {
let data: [Datum]
}
struct League: Codable {
let name: String
let sport: String
let website: URL
}
extension League: Displayable {
var text: String { name }
var image: URL { website }
}
struct Player: Codable {
let name: String
let position: String
let picture: URL
}
extension Player: Displayable {
var text: String { name }
var image: URL { picture }
}
struct Team: Codable {
let name: String
let state: String
let logo: URL
}
extension Team: Displayable {
var text: String { name }
var image: URL { logo }
}
enum Datum: Decodable {
case league(League)
case player(Player)
case team(Team)
enum DatumType: String, Decodable {
case league
case player
case team
}
private enum CodingKeys : String, CodingKey { case type, info }
init(from decoder : Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(DatumType.self, forKey: .type)
switch type {
case .league:
let item = try container.decode(League.self, forKey: .info)
self = .league(item)
case .player:
let item = try container.decode(Player.self, forKey: .info)
self = .player(item)
case .team:
let item = try container.decode(Team.self, forKey: .info)
self = .team(item)
}
}
}
protocol Displayable {
var text: String { get }
var image: URL { get }
}
struct Model<T: Displayable> {
let text: String
let image: URL
init(item: T) {
self.text = item.text
self.image = item.image
}
}
do {
let response = try JSONDecoder().decode(Response.self, from: Data(jsonString.utf8))
let items = response.data
let models = items.map { (item) -> Model<Displayable> in // error: only struct/enum/class types can conform to protocols
switch item {
case .league(let league):
return Model(item: league)
case .player(let player):
return Model(item: player)
case .team(let team):
return Model(item: team)
}
}
} catch {
print(error)
}
推荐答案
此处不需要泛型.
更改模型以接受init中符合Displayable的任何类型
Change Model to accept any type that conforms to Displayable in the init
struct Model {
let text: String
let image: URL
init(item: Displayable) {
self.text = item.text
self.image = item.image
}
}
,然后将闭包更改为返回模型
and then change the closure to return Model
let models = items.map { (item) -> Model in
如果要保持Model结构的通用性,则需要将 map
调用更改为
let models: [Any] = items.map { item -> Any in
switch item {
case .league(let league):
return Model(item: league)
case .player(let player):
return Model(item: player)
case .team(let team):
return Model(item: team)
}
}
当符合 CustomStringConvertible
extension Model: CustomStringConvertible {
var description: String {
"\(text) type:\(type(of: self))"
}
}
print(models)
[NBA类型:Model< League>,Kawhi Leonard类型:Model< Player> ;,洛杉矶快船类型:Model< Team>]
[NBA type:Model<League>, Kawhi Leonard type:Model<Player>, Los Angeles Clippers type:Model<Team>]
这篇关于无法将符合协议的元素映射到通用元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!