可编码结构包含协议属性

Tim*_*Tim 5 xcode swift

我有一个从可编码继承的协议

protocol OrderItem:Codable {
    var amount:Int{get set}
    var isPaid:Bool{get set}
} 
Run Code Online (Sandbox Code Playgroud)

结构符合此协议

struct ProductItem:OrderItem {
    var amount = 0
    var isPaid = false
    var price = 0.0
}
Run Code Online (Sandbox Code Playgroud)

然而,当我把这个结构放入可编码的结构时,我得到了错误

struct Order:Codable {
    var id:String
    var sn:String = ""
    var items:[OrderItem] = []
    var createdAt:Int64 = 0
    var updatedAt:Int64 = 0
}
Run Code Online (Sandbox Code Playgroud)

错误是

Type 'Order' does not conform to protocol 'Encodable'
Type 'Order' does not conform to protocol 'Decodable'
Run Code Online (Sandbox Code Playgroud)

但如果我更改项目:[OrderItem]到项目:[ProductItem],一切正常!

我该如何解决这个问题?

Pin*_*ink 8

你不能这样做,因为协议只陈述你必须做的事情.因此,当您遵循协议XCodable,它只意味着符合的任何类型也X必须符合,Codable但它不会提供所需的实现.您可能感到困惑,因为Codable当您的所有类型都已经存在时,您不需要执行任何操作Codable.如果Codable要求你说实现一个被调用的函数myFunction,你OrderItem就不会缺少该函数的实现.

以下是您可以做的事情:

struct Order<T: OrderItem>: Codable {
   var id:String
   var sn:String = ""
   var items: [T] = []
   var createdAt:Int64 = 0
   var updatedAt:Int64 = 0
}
Run Code Online (Sandbox Code Playgroud)

你现在说这items是一个符合的泛型类型OrderItem.


Oha*_*adM 5

值得一提的是,如果您有一个数组的属性并且类型是一个协议:let arrayProtocol: [MyProtocol]并且该数组包含多个都符合 的类型MyProtocol,则您必须实现自己的init(from decoder: Decoder) throws来获取值并func encode(to encoder: Encoder) throws对其进行编码。

例如:

protocol MyProtocol {}
struct FirstType: MyProtocol {}
struct SecondType: MyProtocol {}

struct CustomObject: Codable {
   let arrayProtocol: [MyProtocol]

   enum CodingKeys: String, CodingKey {
      case firstTypeKey
      case secondTypeKey
   }
}
Run Code Online (Sandbox Code Playgroud)

所以我们的解码看起来像这样:

init(from decoder: Decoder) throws {
   let values = try decoder.container(keyedBy: CodingKeys.self)
   // FirstType conforms to MyProtocol
   let firstTypeArray = try values.decode([FirstType].self, forKey: .firstTypeKey)
   // SecondType conforms to MyProtocol
   let secondTypeArray = try values.decode([SecondType].self, forKey: .secondTypeKey)
   // Our array is finally decoded
   self.arrayProtocol: [MyProtocol] = firstTypeArray + secondTypeArray
}
Run Code Online (Sandbox Code Playgroud)

与编码相同,我们需要在编码之前转换为实际类型:

func encode(to encoder: Encoder) throws {
   var container = encoder.container(keyedBy: CodingKeys.self)
   let firstActualTypeArray = arrayProtocol.compactMap{$0 as? FirstType}
   let secondActualTypeArray = arrayProtocol.compactMap{$0 as? SecondType}

   try container.encode(firstActualTypeArray, forKey: .firstTypeKey)
   try container.encode(secondActualTypeArray, forKey: .secondTypeKey)
}
Run Code Online (Sandbox Code Playgroud)