The structure to be encoded contains a protocol property

I have a protocol that inherits from codable

protocol OrderItem:Codable {
    var amount:Int{get set}
    var isPaid:Bool{get set}
} 

      

And the structure conforms to this protocol

struct ProductItem:OrderItem {
    var amount = 0
    var isPaid = false
    var price = 0.0
}

      

However, when I put this structure in the coded structure I was getting errors

struct Order:Codable {
    var id:String
    var sn:String = ""
    var items:[OrderItem] = []
    var createdAt:Int64 = 0
    var updatedAt:Int64 = 0
}

      

Errors

Type 'Order' does not conform to protocol 'Encodable'
Type 'Order' does not conform to protocol 'Decodable'

      

But if I change items: [OrderItem] to items: [ProductItem] everything works!

How can I fix this problem?

+5


source to share


3 answers


You cannot do this because the protocol only states what you have to do. So when you match your protocol X

with Codable

, it only means that any type that matches X

must also match, Codable

but it will not provide the required implementation. You are probably confused because it Codable

doesn't require you to implement anything when all your types are already Codable

. If I Codable

asked you to, say, implement a function myFunction

, you would OrderItem

not OrderItem

have an implementation of that function, and the compiler would force you to add it.

Here's what you can do instead:



struct Order<T: OrderItem>: Codable {
   var id:String
   var sn:String = ""
   var items: [T] = []
   var createdAt:Int64 = 0
   var updatedAt:Int64 = 0
}

      

Now you are saying that items

is a generic type that matches OrderItem

.

+7


source


Codable is a type alias for encoding and decoding.

Therefore, if you are implementing it, you need to implement the following two functions.



  • func encode(to: Encoder)

  • init(from: Decoder)

0


source


It's worth noting that if you have an array property and the type is a protocol: let arrayProtocol: [MyProtocol]

and the array contains multiple types, all of which match MyProtocol

, you will have to implement your own init(from decoder: Decoder) throws

to get the values ​​and func encode(to encoder: Encoder) throws

to encode them.

For example:

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

struct CustomObject: Codable {
   let arrayProtocol: [MyProtocol]

   enum CodingKeys: String, CodingKey {
      case firstTypeKey
      case secondTypeKey
   }
}

      

so our decoder will look like this:

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
}

      

and the same for the encoded one, we have to cast to the actual type before encoding:

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

      

0


source







All Articles