abcross92
abcross92

Reputation: 33

Genericize method to encode a `Codable` protocol

import Foundation

protocol ProtocolA: Codable {
    var var1: String { get }
}

struct Type1: ProtocolA {
    var var1: String
    var var2: Int
}

struct Type2: ProtocolA {
    var var1: String
    var var2: Bool
}

func encode<T: ProtocolA & Encodable>(object: T) throws -> Data {
    return try JSONEncoder().encode(object as! T.Type)
}

Putting the above in a playground results in error: argument type 'T.Type' does not conform to expected type 'Encodable'

Why does this happen when I am saying that T has to conform to Encodable?

Upvotes: 1

Views: 69

Answers (2)

Awad
Awad

Reputation: 921

Your compiler is actually complaining about the type cast your doing at the end which is converting object to the metatype of T, in your example either Type1.Type or Type2.Type.

From the encoding perspective, what the compiler needs to know is the model confirms to Encodable which is implicit in the T: Codable statement.

import Foundation

protocol ProtocolA: Codable {
    var var1: String { get }
}

struct Type1: ProtocolA {
    var var1: String
    var var2: Int
}

struct Type2: ProtocolA {
    var var1: String
    var var2: Bool
}

func encode<T: Codable>(object: T) throws -> Data {
    return try JSONEncoder().encode(object)
}

let type2 = Type2(var1: "test1", var2: true)

print(type2)

Upvotes: 1

Rob Napier
Rob Napier

Reputation: 299663

return try JSONEncoder().encode(object as! T.Type)

This means to convert object to the metatype of T. The type of the type. For example, 1 is an Int. But "Int" itself has a type, which is Int.Type, which is a metatype. Metatypes do not conform to Encodable.

You meant:

return try JSONEncoder().encode(object as! T)

But you really just meant:

return try JSONEncoder().encode(object)

since object is always of type T. That's its explicit type from the function signature. Since you also don't rely on ProtocolA for this algorithm, this all boils down to:

func encode<T: Encodable>(object: T) throws -> Data {
    return try JSONEncoder().encode(object)
}

Upvotes: 3

Related Questions