简体   繁体   English

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

[英]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要解码StickerStringStickerBitmap类型的数组, 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
    }

}
  • through this class you can pass any type or even list that conforms to Codable通过这个类,你可以传递符合 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. 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.

相关问题 swift 将字典转换为 jsonString 错误:协议类型 'Any' 不能符合 'Encodable' 因为只有具体类型才能符合协议 - swift Convert dictionary to jsonString error : Protocol type 'Any' cannot conform to 'Encodable' because only concrete types can conform to protocols 协议类型“Encodable”的值不能符合“Encodable”; 只有结构/枚举/类类型可以符合协议 - Value of protocol type 'Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols 协议类型“Any”的值不能符合“Equatable”; 只有结构/枚举/类类型可以符合协议 - Value of protocol type 'Any' cannot conform to 'Equatable'; only struct/enum/class types can conform to protocols 我收到错误“类型'(UITextRange)-&gt;字符串?' 不能符合“BinaryInteger”; 只有结构/枚举/类类型可以符合协议” - I got error “Type '(UITextRange) -> String?' cannot conform to 'BinaryInteger'; only struct/enum/class types can conform to protocol” 类型 &#39;()&#39; 不能符合 &#39;View&#39;; 只有 struct/enum/class 类型才能符合协议; 使用 SwiftUI 调用函数 - Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols; calling functions with SwiftUI navigationBarItems“类型[视图]不能符合'视图'; 只有结构/枚举/类类型可以符合协议” - navigationBarItems “Type [view] cannot conform to 'View'; only struct/enum/class types can conform to protocols” 类型 '()' 不能符合 'View'; 只有结构/枚举/类类型可以符合协议 - Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols 协议必须符合 NSObject 协议吗? - Must Protocols Conform To The NSObject Protocol? Swift 错误:“类型 &#39;()&#39; 不能符合 &#39;View&#39;;只有 struct/enum/class 类型可以符合协议”调用函数写入文本时 - Swift error: "Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols" when calling function to write text 类型不符合协议 - Type does not conform to protocol
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM