Search code examples
swiftencodable

Encode an array of items conforming to an Encodable protocol


I was surprised recently to find that this:

protocol A: Encodable {}

class B: A {
    var x = 3
}

class C: A {
    var y = 4
}

let items: [A] = [B()]
let encoded = try JSONEncoder().encode(items)

Fails in Swift with:

Type 'any A' cannot conform to 'Encodable'

while this works:

let items: [B] = [B()]
let encoded = try JSONEncoder().encode(items)

Apparently the issue is that protocols do not conform to themselves. A workaround is provided in this answer.

I understand the reasoning and the workaround in the answers, but it is still quite inconvenient. Both of those answers are from ~2017; is there a cleaner way to solve this in swift 5?


Solution

  • Sample workaround :)

    struct AnyEncodable: Encodable {
        
        let item: any Encodable
        
        func encode(to encoder: any Encoder) throws {
            var container = encoder.singleValueContainer()
            try container.encode(self.item)
        }
    }
    
    let items: [A] = [B()]
    let encoded = try JSONEncoder().encode(items.map { AnyEncodable(item: $0) })