Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to get data by encode model which conforms to Encodable protocol. But it's failed to invoke func encode like code below:

// MARK: - Demo2

class TestClass2: NSObject, Encodable {
    var x = 1
    var y = 2
}


var dataSource2: Encodable?

dataSource2 = TestClass2()

// error: `Cannot invoke 'encode' with an argument list of type '(Encodable)'`
let _ = try JSONEncoder().encode(dataSource2!)
//func encode<T>(_ value: T) throws -> Data where T : Encodable

But in another demo, it works well, why?

// MARK: - Demo1

protocol TestProtocol {
    func test()
}

class TestClass1: NSObject, TestProtocol {
    func test() {
        print("1")
    }

    var x = 1
    var y = 2
}


var dataSource1: TestProtocol?

dataSource1 = TestClass1()


func logItem(_ value: TestProtocol) {
    value.test()
}

logItem(dataSource1!)
like image 532
AnZ Avatar asked Jun 27 '18 08:06

AnZ


2 Answers

Solution 1.

Try this code, which extend encodable

extension Encodable {
    func toJSONData() -> Data? { 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?{ try? JSONEncoder().encode(self) }
}

Use

var dataSource2: Encodable?
dataSource2 = TestClass2()
let data = dataSource2?.toJSONData()
like image 66
SPatel Avatar answered Oct 21 '22 15:10

SPatel


You can't pass a protocol but you can use generics to require a class that conforms to one:

func printJSON<T: Encodable>(_ data: T) {
    if let json = try? JSONEncoder().encode(data) {
        if let str = String(data: json, encoding: .utf8) {
            print(str)
        }
    }
}

// Now this should work
var dataSource2 = TestClass2()
printJSON(dataSource2!)
like image 34
stevex Avatar answered Oct 21 '22 15:10

stevex