Why can not use protocol `Encodable` as a type in the func

一曲冷凌霜 提交于 2019-11-28 11:12:24

Solution 1.

Try this code, which extend encodable

extension Encodable {
    func toJSONData() -> Data? {
        return try? JSONEncoder().encode(self)
    }
}

Solution 2.

To avoid polluting Apple-provided protocols with extensions

protocol MyEncodable: Encodable {
    func toJSONData() -> Data?
}

extension MyEncodable {
    func toJSONData() -> Data?{
        return try? JSONEncoder().encode(self)
    }
}

Use

var dataSource2: Encodable?
dataSource2 = TestClass2()
let data = dataSource2?.toJSONData()

Your 2 examples are different.

JSONEncoder().encode() expects a concrete class which conforms to the procotol Encodable. The reference dataSource2 holds a protocol and not a concrete class.

logItem on the other hands, only takes a protocol as input, and NO concrete class which conforms to the protocol. This is the difference between your examples and why your second case is working and the first case does not.

With your current setup, it will not work. You need to pass in a concrete class to the JSONEncoder.

There are a number of approaches to solving this problem.

@SPatel solution of extending Encodable is one possibility. However, I personally try to avoid polluting Apple-provided protocols with extensions.

If I am reading between the lines, it appears what you are wanting is to pass any construct that conforms to Encodable to a function/method in some other struct/class.

Let's take an example of what I think you are trying to achieve:

struct Transform {
    static func toJson(encodable: Encodable) throws -> Data {
        return try JSONEncoder().encode(encodable)
    }
}

However, Xcode will complain:

Protocol type 'Encodable' cannot conform to 'Encodable' because only concrete types can conform to protocols

A Swift-ier solution is to use a constrained generic on the function:

struct Transform {
    static func toJson<EncodableType: Encodable>(encodable: EncodableType) throws -> Data {
        return try JSONEncoder().encode(encodable)
    }
}

Now the compiler can infer the type that conforms to Encodable, and we can call the function as intended:

let dataSource = TestClass2()
let jsonData = try? Transform.toJson(encodable: dataSource)

As long as TestClass2 is Encodable you can use following code. encode should know what to encode. It refers to class properties to do that. Encodable itself doesn't contain any properties.

JSONEncoder().encode(dataSource2 as! TestClass2)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!