Swift: передача типа из свойства в универсальную функцию - PullRequest
0 голосов
/ 05 июля 2018

Для моего сетевого модуля у меня есть этот протокол, который я использую для доступа к различным частям API:

protocol Router: URLRequestConvertible {
    var baseUrl: URL { get }        
    var route: Route { get }        
    var method: HTTPMethod { get }        
    var headers: [String: String]? { get }        
    var encoding: ParameterEncoding? { get }        
    var responseResultType: Decodable.Type? { get }
}

Я принимаю это с перечислениями, которые выглядят так:

enum TestRouter: Router {
    case getTestData(byId: Int)
    case updateTestData(byId: Int)

    var route: Route {
        switch self {
        case .getTestData(let id): return Route(path: "/testData/\(id)")
        case .updateTestData(let id): return Route(path: "/testDataOtherPath/\(id)")
        }
    }

    var method: HTTPMethod {
        switch self {
        case .getTestData: return .get
        case .updateTestData: return .put
        }
    }

    var headers: [String : String]? {
        return [:]
    }

    var encoding: ParameterEncoding? {
        return URLEncoding.default
    }

    var responseResultType: Decodable.Type? {
        switch self {
        case .getTestData: return TestData.self
        case .updateTestData: return ValidationResponse.self
        }
    }
}

Я хочу использовать Codable для декодирования вложенных ответов Api. Каждый ответ состоит из токена и результата, содержание которого зависит от маршрута запроса.

Для выполнения запроса я хочу использовать тип, указанный в свойстве responseResultType в enum выше.

struct ApiResponse<Result: Decodable>: Decodable {
    let token: String
    let result: Result
}

extension Router {
    func asURLRequest() throws -> URLRequest {
        // Construct URL
        var completeUrl = baseUrl.appendingPathComponent(route.path, isDirectory: false)
        completeUrl = URL(string: completeUrl.absoluteString.removingPercentEncoding ?? "")!
        // Create URL Request...
        var urlRequest = URLRequest(url: completeUrl)
        // ... with Method
        urlRequest.httpMethod = method.rawValue
        // Add headers
        headers?.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key) }
        // Encode URL Request with the parameters
        if encoding != nil {
            return try encoding!.encode(urlRequest, with: route.parameters)
        } else {
            return urlRequest
        }
    }

    func requestAndDecode(completion: @escaping (Result?) -> Void) {
        NetworkAdapter.sessionManager.request(urlRequest).validate().responseData { response in
            let responseObject = try? JSONDecoder().decode(ApiResponse<self.responseResultType!>, from: response.data!)
            completion(responseObject.result)
        }
    }
}

Но в моем методе requestAndDecode Выдает ошибку компилятора (Cannot invoke 'decode' with an argument list of type '(Any.Type, from: Data)'). Я не могу использовать ApiResponse<self.responseResultType!> таким образом.

Я мог бы сделать эту функцию универсальной и вызвать ее так:

TestRouter.getTestData(byId: 123).requestAndDecode(TestData.self, completion:)

но тогда мне придется передавать тип ответа каждый раз, когда я хочу использовать эту конечную точку.

Я хочу добиться, чтобы функция расширения requestAndDecode брала информацию о типе ответа из себя, свойство responseResultType.

Возможно ли это?

1 Ответ

0 голосов
/ 05 июля 2018

Игнорируя фактическое сообщение об ошибке, у вас есть фундаментальная проблема с requestAndDecode: это универсальная функция, параметры типа которой определяются на сайте вызова, которая объявляется как возвращающая значение типа Result, но пытается вернуть значение типа self.responseResultType, значение которого равно неизвестно тип.

Если бы система типов Swift поддерживала это, потребовалась бы проверка типов во время выполнения, возможный сбой , и ваш код должен был бы это обработать. Например. Вы можете передать TestData на requestAndDecode, тогда как responseResultType может быть ValidationResponse ...

Измените вызов JSON на:

JSONDecoder().decode(ApiResponse<Result>.self ...

и типы статически совпадают (хотя фактический тип, который является Result, неизвестен).

Вы должны переосмыслить свой дизайн. НТН

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...