Swift Generics — передать несколько типов от вызывающего
У меня есть функция для общения с моим сервером REST следующим образом
func send<T: Decodable>(_ request: HTTPSClient.Request) async throws -> T {
do {
let (data, status): (Data, HTTPSClient.StatusCode) = try await request.send()
if status.responseType != .success { // success means 2xx
throw try JSONDecoder().decode(CustomError.self, from: data)
}
return try JSONDecoder().decode(T.self, from: data)
} catch {
// some error handling here
}
}
И называется следующим образом
public struct API1Response: Codable {
// some fields
}
...
let response: API1Response = try await self.send(httpsRequest)
Теперь у меня есть особый вариант использования, когда ответ должен быть декодирован JSON в разные структуры на основе кода состояния ответа HTTP (2xx). Например, если код ответа 200 OK, его необходимо расшифровать вstruct APIOKResponse
. Если код ответа 202 Accepted, его необходимо декодировать вstruct APIAcceptedResponse
и так далее.
Я хочу написать аналогичную функцию, как указано выше, которая может поддерживать несколько типов ответов.
Я написал приведенную ниже функцию, она не выдает ошибок компиляции.
func send<T: Decodable>(_ request: HTTPSClient.Request, _ types: [HTTPSClient.StatusCode: T.Type]) async throws -> T {
do {
let (data, status): (Data, HTTPSClient.StatusCode) = try await request.send()
if status.responseType != .success { // success means 2xx
throw try JSONDecoder().decode(CustomError.self, from: data)
}
guard let t = types[status] else {
throw ClientError.unknownResponse
}
return try JSONDecoder().decode(t.self, from: data)
} catch {
// some error handling here
}
}
Хотя я не понимаю, как это назвать. Пробовал ниже
struct APIAcceptedResponse: Codable {
// some fields
}
struct APIOKResponse: Codable {
// some fields
}
...
let response = try await self.send(httpsRequest, [.accepted: APIAcceptedResponse, .ok: APIOKResponse])
// and
let response = try await self.send(httpsRequest, [.accepted: APIAcceptedResponse.self, .ok: APIOKResponse.self])
Но в обоих случаях выдает ошибку
Cannot convert value of type 'APIAcceptedResponse.Type' to expected dictionary value type 'APIOKResponse.Type'
- Есть ли что-то, что я делаю неправильно в
send
сама функция? - Если нет, то как это назвать?
- Это что-то может быть достигнуто даже?
1 ответ
Это не может быть решено с помощью дженериков.T
должен быть определенного типа. Таким образом, вы не можете предоставить массив из нескольких различных типов, из которых может выбирать функция. Также этот тип должен быть известен во время компиляции. Вы не можете выбрать его в зависимости от вашего ответа.
В качестве альтернативы вы можете использовать протоколы.
Простой пример:
protocol ApiResponse: Decodable{
// common properties of all responses
}
struct ApiOkResponse: Codable, ApiResponse{
}
struct ApiErrorResponse: Codable, ApiResponse{
}
func getType(_ statusCode: Int) -> ApiResponse.Type{
if statusCode == 200{
return ApiOkResponse.self
} else {
return ApiErrorResponse.self
}
}
func send() throws -> ApiResponse{
let data = Data()
let responseStatusCode = 200
let type = getType(responseStatusCode)
return try JSONDecoder().decode(type, from: data)
}