0

I have struct that has parameter with protocol type

protocol CheckoutOrder {}

struct CheckoutOrderEntity: Encodable {
    private enum CodingKeys: String, CodingKey {
        case amount
        case payment
    }
    
    let amount: Int
    let payment: CheckoutOrder
}

Also have 2 structs which implements protocol CheckoutOrder

struct ApplePayOrderEntity: Encodable {
    private enum CodingKeys: String, CodingKey {
        case token
    }
    
    let token: ApplePayTokenEntity
}

extension ApplePayOrderEntity: CheckoutOrder { }
struct CheckoutPaymentEntity: Encodable {
    private enum CodingKeys: String, CodingKey {
        case terminalName
        case shouldUseBalance
    }
    
    let shouldUseBalance: Bool
    let terminalName: String
}

extension CheckoutPaymentEntity: CheckoutOrder { }

I have an error: Type 'CheckoutOrderEntity' does not conform to protocol 'Encodable' and CheckoutOrder protocol requires function 'encode(to:)'.... So, the main reason of using protocol is that payment can be type of ApplePayOrderEntity or CheckoutPaymentEntity. How to solve problem or what is best practice in this case?

  • The linked question is for arrays, but you can convert it to just handle a single element by using `try container.encode(payment, forKey: .payment)`. It just won't write an automatic conformance for you. – Rob Napier Aug 09 '22 at 13:58
  • (You also need to add `: Encodable` to the protocol definition as Joakim Danielson notes. But the deeper issue is that you won't get automatic conformance because the compiler just won't do that today.) – Rob Napier Aug 09 '22 at 14:06

1 Answers1

2

First you need to make the protocol extend Encodable to tell the compiler that anything that conforms to CheckoutOrder also conforms to Encodable

protocol CheckoutOrder: Encodable {}

Then I would use generics for this and change CheckoutOrderEntity to

struct CheckoutOrderEntity<Payment: CheckoutOrder>: Encodable {
    private enum CodingKeys: String, CodingKey {
        case amount
        case payment
    }

    let amount: Int
    let payment: Payment
}

Example

let checkout = CheckoutOrderEntity(amount: 1000,
                                   payment: CheckoutPaymentEntity(shouldUseBalance: true,
                                                                  terminalName: "terminal"))
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52