Как ссылаться на свойство протокола, который является типом Associated? - PullRequest
0 голосов
/ 08 мая 2019

У меня есть класс NetworkProvider, который обрабатывает мои сетевые запросы и создается с помощью associatedtype протокола TargetType, который определяет, что необходимо для конечной точки.

Сейчас я использую NetworkProvider следующим образом:

let provider = NetworkProvider<IMDBAPI>()
provider.request<[Movie]>(IMDBAPI.getTop250Movies){
    //When successful returns HTTPResponse.success([Movie])
}

Сейчас request<T: Decodable> должен ссылаться на декодируемую модель. Однако я хотел бы иметь возможность сделать вывод из IMDBAPI.getTop250Movies, поскольку он всегда будет возвращать массив объектов Movie, поэтому кажется неэффективным требовать от разработчика предварительного знания того, что является типом возврата конечной точки до его использования.

Можно ли вывести модель, возвращаемую Endpoint / TargetType, через переданный ей протокол ассоциированного типа?


Код

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)
        })
    }
}

Пример реализации TargetType

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"]
    }
}

1 Ответ

1 голос
/ 08 мая 2019

Надеюсь, это поможет. Пожалуйста, просмотрите следующий комментарий, встроенный в код, который я прикрепил ...

// 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)
...