[英]Protocol type cannot conform to protocol because only concrete types can conform to protocols
Within the app, we have two types of Stickers, String and Bitmap.在应用程序中,我们有两种类型的贴纸,字符串和位图。 Each sticker pack could contain both types.
每个贴纸包可以包含这两种类型。 This is how I declare the models:
这就是我声明模型的方式:
// Mark: - Models
protocol Sticker: Codable {
}
public struct StickerString: Sticker, Codable, Equatable {
let fontName: String
let character: String
}
public struct StickerBitmap: Sticker, Codable, Equatable {
let imageName: String
}
After the user chooses some stickers and used them, we want to save the stickers into UserDefaults
so we can show him the "Recently Used" Sticker tab.在用户选择一些贴纸并使用它们后,我们希望将贴纸保存到
UserDefaults
以便我们可以向他显示“最近使用的”贴纸选项卡。 I'm trying to Decode the saved [Sticker]
array:我正在尝试解码保存的
[Sticker]
数组:
let recentStickers = try? JSONDecoder().decode([Sticker].self, from: data)
But I get the following compile error:但我收到以下编译错误:
Protocol type 'Sticker' cannot conform to 'Decodable' because only concrete types can conform to protocols
I can't understand why as I declared Sticker
as Codable
which also implement Decodable
.我不明白为什么我将
Sticker
声明为Codable
,它也实现了Decodable
。 Any help would be highly appreciated!任何帮助将不胜感激!
Rather than protocols use generics.而不是协议使用泛型。
Declare a simple function声明一个简单的函数
func decodeStickers<T : Decodable>(from data : Data) throws -> T
{
return try JSONDecoder().decode(T.self, from: data)
}
T
can be a single object as well as an array. T
可以是单个对象也可以是数组。
In your structs drop the Sticker
protocol.在您的结构中删除
Sticker
协议。 You can also delete Equatable
because it's getting synthesized in structs.您还可以删除
Equatable
因为它正在结构中合成。
public struct StickerString : Codable {
let fontName: String
let character: String
}
public struct StickerBitmap : Codable {
let imageName: String
}
To decode one of the sticker types annotate the type要解码其中一种贴纸类型,请对该类型进行注释
let imageStickers = """
[{"imageName":"Foo"},{"imageName":"Bar"}]
"""
let stickerData = Data(imageStickers.utf8)
let recentStickers : [StickerBitmap] = try! decodeStickers(from: stickerData)
print(recentStickers.first?.imageName)
and和
let stringSticker = """
{"fontName":"Times","character":"😃"}
"""
let stickerData = Data(stringSticker.utf8)
let sticker : StickerString = try! decodeStickers(from: stickerData)
print(sticker.character)
To decode an array of StickerString
and StickerBitmap
types declare a wrapper enum with associated values要解码
StickerString
和StickerBitmap
类型的数组, StickerString
声明具有关联值的包装器枚举
enum Sticker: Codable {
case string(StickerString)
case image(StickerBitmap)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let stringData = try container.decode(StickerString.self)
self = .string(stringData)
} catch DecodingError.keyNotFound {
let imageData = try container.decode(StickerBitmap.self)
self = .image(imageData)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let string) : try container.encode(string)
case .image(let image) : try container.encode(image)
}
}
}
Then you can decode然后就可以解码了
let stickers = """
[{"imageName":"Foo"},{"imageName":"Bar"}, {"fontName":"Times","character":"😃"}]
"""
let stickerData = Data(stickers.utf8)
let recentStickers = try! JSONDecoder().decode([Sticker].self, from: stickerData)
print(recentStickers)
In a table view just switch
on the enum在表格视图中只需
switch
枚举
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sticker = stickers[indexPath.row]
switch sticker {
case .string(let stringSticker):
let cell = tableView.dequeueReusableCell(withCellIdentifier: "StringStickerCell", for: indexPath) as! StringStickerCell
// update UI
return cell
case .image(let imageSticker):
let cell = tableView.dequeueReusableCell(withCellIdentifier: "ImageStickerCell", for: indexPath) as! ImageStickerCell
// update UI
return cell
}
}
what happens here is kind of self explanatory这里发生的事情是一种自我解释
JSONDecoder().decode(/* swift here is expecting class or struct that conforms to Codable */.self, from: data)
but let us assume that you can pass a protocol in your protocol但让我们假设你可以在你的协议中传递一个协议
protocol Sticker: Codable {
}
where is the properties that you are expecting swift to decode from data ?您期望 swift 从数据中解码的属性在哪里?
you added the properties in你添加了属性
public struct StickerString: Sticker, Codable, Equatable { // it should have redundendant conformance as well as you are conforming to Coddle again
let fontName: String // here is the properties you are expected to be decoded with the coding keys
let character: String // here is the properties you are expected to be decoded with the coding keys
}
Here is what I suggest you do as long you want the type you want to decode to be dynamic只要您希望要解码的类型是动态的,我建议您这样做
class GenericService< /* here you can pass your class or struct that conforms to Codable */ GenericResponseModel: Codable> {
func buildObjectFromResponse(data: Data?) -> GenericResponseModel? {
var object : GenericResponseModel?
do {
object = try JSONDecoder().decode(GenericResponseModel.self , from: data!)
} catch (let error){
print(error)
}
return object
}
}
private func handleDecodingTypes (stickers: [Sticker]){
for sticker in stickers {
if sticker is StickerString {
/* do the decoding here */
}
if sticker is StickerBitmap {
/* do the decoding here */
}
}
}
The Sticker protocol is not confirming/implementing the Codable, it is actually inheriting from the Codable. Sticker 协议不是确认/实现 Codable,它实际上是从 Codable 继承的。 As the error message suggests, Protocols do not conform to other protocols only concrete types do.
正如错误消息所暗示的那样,协议不符合其他协议,只有具体类型才符合。
protocol Sticker: Codable //This is Protocol inheritance
By stating通过说明
public struct StickerString: Sticker
Means that Sticker string conforms to Sticker and the Sticker is child of Codable, so StickerString eventually conforms to Codable.意味着 Sticker 字符串符合 Sticker 并且 Sticker 是 Codable 的孩子,所以 StickerString 最终符合 Codable。 There is no need to state conformance again ie:
无需再次声明一致性,即:
public struct StickerString: Sticker, Codable //Conformance to Codable is redundant
Now coming to the decoding part.现在进入解码部分。
let recentStickers = try? JSONDecoder().decode([Sticker].self, from: data)
The decode method needs a concrete type. decode 方法需要一个具体的类型。 It doesn't have any information about the underlying type or its attributes because Sticker is just a protocol itself inheriting from Codable that has no properties/attributes.
它没有关于底层类型或其属性的任何信息,因为 Sticker 只是一个协议本身,它继承自 Codable,没有任何属性/属性。 Compiler would not have any problem combining both the StrickerString and StickerBitmap after they've decoded eg
编译器在解码后结合 StrickerString 和 StickerBitmap 不会有任何问题,例如
let stickerString = try JSONDecoder().decode(StickerString.self, from: data)
let stickerBitmap = try JSONDecoder().decode(StickerBitmap.self, from: data)
let stickers : [Sticker] = [stickerString, stickerBitmap]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.