Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Value of protocol type 'Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols

I have the following Swift code

func doStuff<T: Encodable>(payload: [String: T]) {
    let jsonData = try! JSONEncoder().encode(payload)
    // Write to file
}

var things: [String: Encodable] = [
    "Hello": "World!",
    "answer": 42,
]

doStuff(payload: things)

results in the error

Value of protocol type 'Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols

How to fix? I guess I need to change the type of things, but I don't know what to.

Additional info:

If I change doStuff to not be generic, I simply get the same problem in that function

func doStuff(payload: [String: Encodable]) {
    let jsonData = try! JSONEncoder().encode(payload) // Problem is now here
    // Write to file
}
like image 360
Mathias Bak Avatar asked Jun 07 '20 22:06

Mathias Bak


People also ask

Can a protocol be Codable Swift?

Codable is the combined protocol of Swift's Decodable and Encodable protocols. Together they provide standard methods of decoding data for custom types and encoding data to be saved or transferred.

What is Codable?

Codable is a type alias for the Encodable and Decodable protocols. When you use Codable as a type or a generic constraint, it matches any type that conforms to both protocols.


2 Answers

Encodable cannot be used as an annotated type. It can be only used as a generic constraint. And JSONEncoder can encode only concrete types.

The function

func doStuff<T: Encodable>(payload: [String: T]) {

is correct but you cannot call the function with [String: Encodable] because a protocol cannot conform to itself. That's exactly what the error message says.


The main problem is that the real type of things is [String:Any] and Any cannot be encoded.

You have to serialize things with JSONSerialization or create a helper struct.

like image 67
vadian Avatar answered Oct 23 '22 08:10

vadian


You can use the where keyword combined with Value type, like this:

func doStuff<Value>(payload: Value) where Value : Encodable {
    ...
}
like image 5
Ely Avatar answered Oct 23 '22 07:10

Ely