Hello, I have a question regarding generics overloading.
I faced a problem in my project. I tried to handle Data
differently and it worked perfectly until I wrapped it in another generic layer.
My understanding of generics is that generic type is a concrete type.
In the example below I expected to have all the prints to be equal to the first one.
So, is it expected behaviour or a bug?
import Foundation
@available(iOS 8, *)
public protocol APICall {
var url: URL { get }
func body(using jsonEncoder: JSONEncoder) throws -> Data?
}
public extension APICall {
func body(using jsonEncoder: JSONEncoder) throws -> Data? { nil }
}
@available(iOS 8, *)
public extension URLRequest {
init<Endpoint: APICall>(endpoint: Endpoint) {
self.init(url: endpoint.url)
}
init<Endpoint: APICall>(endpoint: Endpoint, bodyEncoder: JSONEncoder) throws {
self.init(endpoint: endpoint)
self.httpBody = try endpoint.body(using: bodyEncoder)
}
init(endpoint: EmbodiedEndpoint<Data>, bodyEncoder: JSONEncoder) throws {
self.init(endpoint: endpoint)
self.httpBody = endpoint.body
}
}
@available(iOS 8, *)
public struct Endpoint: APICall {
public let url: URL
}
@available(iOS 8, *)
public struct EmbodiedEndpoint<Body: Encodable> {
public let url: URL
fileprivate let body: Body
public init(url: URL, body: Body) {
self.url = url
self.body = body
}
}
extension EmbodiedEndpoint: APICall {
public func body(using jsonEncoder: JSONEncoder) throws -> Data? {
try jsonEncoder.encode(body)
}
}
public extension EmbodiedEndpoint<Data> {
func body(using jsonEncoder: JSONEncoder) throws -> Data? { body }
}
struct Request: Codable {
let id: Int
}
struct NetworkService {
let jsonEncoder: JSONEncoder
func call<Endpoint: APICall>(endpoint: Endpoint) throws {
print("Running endpoint of type: \(type(of: endpoint))")
let urlRequest = try URLRequest(endpoint: endpoint, bodyEncoder: jsonEncoder)
print(String(decoding: urlRequest.httpBody!, as: UTF8.self))
}
}
let encoder = JSONEncoder()
let url = URL(string: "https://www.google.com")!
let request = Request(id: 1)
func testEntity() throws {
let endpoint = EmbodiedEndpoint(url: url, body: request)
let urlRequest = try URLRequest(endpoint: endpoint, bodyEncoder: encoder)
print(String(decoding: urlRequest.httpBody!, as: UTF8.self))
}
func testData() throws {
let data = try encoder.encode(request)
let endpoint = EmbodiedEndpoint(url: url, body: data)
let urlRequest = try URLRequest(endpoint: endpoint, bodyEncoder: encoder)
print(String(decoding: urlRequest.httpBody!, as: UTF8.self))
}
func testGenericWrapper() throws {
let networkService = NetworkService(jsonEncoder: encoder)
let data = try encoder.encode(request)
let endpoint = EmbodiedEndpoint(url: url, body: data)
try networkService.call(endpoint: endpoint)
}
try testEntity() // prints {"id":1}
try testData() // prints {"id":1}
try testGenericWrapper() // prints "eyJpZCI6MX0="