Reputation: 4870
I have a NetworkProvider
class that handles my network requests and is instantiated with an associatedtype
of a TargetType
protocol that defines what's needed for an endpoint.
Right now I utilize NetworkProvider as follows:
let provider = NetworkProvider<IMDBAPI>()
provider.request<[Movie]>(IMDBAPI.getTop250Movies){
//When successful returns HTTPResponse.success([Movie])
}
Right now the request<T: Decodable>
must reference a Decodable model. However I'd like to be able to have this inferred from the IMDBAPI.getTop250Movies
as this will always return an array of Movie
objects so it seems counterproductive to require a developer to have advance knowledge of what the return type of an endpoint is prior to utilising it.
Is it possible to infer the model returned by an an Endpoint/TargetType through the associatedtype protocol passed to it?
Code
TargetType
public protocol TargetType {
var baseURL: URL { get }
var path: String { get }
var method: HTTPMethod { get }
var headers: [String: String]? { get }
var task: HTTPRequestTask { get }
//var returnType: Decodable? { get } //This is what I'd like to use.
}
NetworkProvider
public protocol NetworkProviderType {
associatedtype Target: TargetType
}
public class NetworkProvider<Target: TargetType>: KindredProviderType {
public func request<T: Decodable>(_ target: Target, handler: @escaping (HTTPResponse<T>) -> ()) {
let endpoint = NetworkProvider.defaultEndpointMapping(for: target)
guard let urlRequest = try? endpoint.urlRequest() else {
return handler(HTTPResponse.error(HTTPError.failedToCreate(url: endpoint.url)))
}
session.runDataTask(with: urlRequest, completionHandler: { (data, response, error) in
let result = HTTPResponse<T>.createFrom(url: urlRequest.url!, error: error, data: data, response: response)
if case let HTTPResponse.error(error) = result {
loggingPrint(error)
}
handler(result)
})
}
}
Example TargetType Implementation
enum SplunkService {
case recordLogin(report: SplunkJourneyReporting)
case recordGameLaunch(report: SplunkJourneyReporting)
case recordError(report: SplunkEventReport)
}
extension SplunkService: TargetType {
var task: HTTPRequestTask {
switch self {
case .recordError(let report):
return HTTPRequestTask.requestBodyParameters(report.toDictionary())
case .recordGameLaunch(let report), .recordLogin(let report):
return HTTPRequestTask.requestBodyParameters(report.toDictionary())
}
}
var baseURL: URL { return URL(string: "https://api.unibet.com")! }
var path: String {
switch self {
case .recordLogin(_):
return "/eum-collector/journeys/login/reports"
case .recordGameLaunch(_):
return "/eum-collector/journeys/game_launch/reports"
case .recordError(_):
return "/eum-collector/events"
}
}
var method: HTTPMethod {
return .post
}
var headers: [String: String]? {
return [HTTPHeaderField.contentType.rawValue: "application/json"]
}
}
Upvotes: 0
Views: 64
Reputation: 77
I hope this helps. Please review the following comment inline with the code I attached...
// the compiler understand exactly what type `T` is because we `init` the class using `T` and that is also the same type used to conform to `TargetType`, in other words if I do `NetworkProvider(returnType: decodableThing)`, the compiler knows that `T` = (the class of `decodableThing`)
import UIKit
import Foundation
public protocol TargetType {
associatedtype ReturnTypeUsed: Decodable
var returnType: ReturnTypeUsed? { get } //This is what I'd like to use.
}
public class NetworkProvider<T> {
// The compiler understand exactly what type `T` is because we `init` the class using `T` and that is also the same type used to conform to `TargetType`
// in other words if I do `NetworkProvider(returnType: decodableThing)`, the compiler knows that `T` = (the class of `decodableThing`)
public typealias ReturnTypeUsed = T
public var returnType: T?
init(returnType: T) {
self.returnType = returnType
}
}
class Movie: Decodable {
}
let someMovie = Movie()
class IMDBAPI: TargetType {
var returnType: Movie?
}
let networkProvider = NetworkProvider(returnType: someMovie)
print(networkProvider.returnType) // returns `Optional(__lldb_expr_5.Movie)
Upvotes: 1