协议类型不能符合协议,因为只有具体类型可以符合协议

时间:2019-07-14 10:17:15

标签: ios swift protocols codable decodable

在应用程序中,我们有两种贴纸类型,即String和Bitmap。每个贴纸包可以包含两种类型。这就是我声明模型的方式:

// 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
}

用户选择了一些贴纸并使用了它们之后,我们要将贴纸保存到UserDefaults中,以便向他显示“最近使用过的”贴纸选项卡。我正在尝试解码保存的[Sticker]数组:

let recentStickers = try? JSONDecoder().decode([Sticker].self, from: data)

但是出现以下编译错误:

Protocol type 'Sticker' cannot conform to 'Decodable' because only concrete types can conform to protocols

我无法理解为什么将Sticker声明为Codable并同时实现Decodable的原因。任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:1)

不是协议使用泛型。

声明一个简单函数

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

T可以是单个对象,也可以是数组。


在您的结构中放入Sticker协议。您还可以删除Equatable,因为它已在结构中合成。

public struct StickerString : Codable {
    let fontName: String
    let character: String
}

public struct StickerBitmap : Codable {
    let imageName: String
}

要解码一种不干胶标签类型,请注释类型

let imageStickers = """
[{"imageName":"Foo"},{"imageName":"Bar"}]
"""    
let stickerData = Data(imageStickers.utf8)

let recentStickers : [StickerBitmap] = try! decodeStickers(from: stickerData)
print(recentStickers.first?.imageName)

let stringSticker = """
{"fontName":"Times","character":"?"}
"""    
let stickerData = Data(stringSticker.utf8)

let sticker : StickerString = try! decodeStickers(from: stickerData)
print(sticker.character)

要解码StickerStringStickerBitmap类型的数组,请声明具有相关值的包装器枚举

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)
        }
    }
}

然后您可以解码

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)

在表视图中,仅枚举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
    }
}

答案 1 :(得分:1)

这里发生的事情是一种自我解释


JSONDecoder().decode(/* swift here is expecting class or struct that conforms to Codable */.self, from: data)

但是让我们假设您可以通过一个协议 在您的协议中

protocol Sticker: Codable {
}

您期望迅速从数据中解码的属性在哪里?

您在

中添加了属性
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
}

这是我建议您执行的操作,只要您希望解码的类型是动态的

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
    }

}
  • 通过此类,您可以传递符合Codable的任何类型或什至列表
  • 然后您将使用以下方法将类型检查与解码过程脱钩
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 */
            }
        }
    }

答案 2 :(得分:0)

Sticker协议没有确认/实现Codable,它实际上是从Codable继承的。如错误消息所示,协议不符合其他协议,只有具体类型符合。

protocol Sticker: Codable //This is Protocol inheritance

通过陈述

public struct StickerString: Sticker

表示Sticker字符串符合Sticker,而Sticker是Codable的子代,因此StickerString最终符合Codable。无需再次声明一致性,即:

public struct StickerString: Sticker,  Codable //Conformance to Codable is redundant

现在进入解码部分。

  let recentStickers = try? JSONDecoder().decode([Sticker].self, from: data)

解码方法需要具体的类型。它没有有关基础类型或其属性的任何信息,因为Sticker只是从Codable继承的协议本身,该协议没有属性/属性。 在将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]