Protocol type cannot conform to protocol because only concrete types can conform to protocols
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.
In your structs drop the Sticker
protocol. You can also delete Equatable
because it's getting synthesized in structs.
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
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
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 protocolin your protocol
protocol Sticker: Codable {}
where is the properties that you are expecting swift to decode from data ?
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 }}
- through this class you can pass any type or even list that conforms to Codable
- then you will decouple the type checking from the decoding process using the method below
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. 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. There is no need to state conformance again i.e:
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. 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.Compiler would not have any problem combining both the StrickerString and StickerBitmap after they've decoded e.g.
let stickerString = try JSONDecoder().decode(StickerString.self, from: data)let stickerBitmap = try JSONDecoder().decode(StickerBitmap.self, from: data)let stickers : [Sticker] = [stickerString, stickerBitmap]