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)
}
Другие вопросы по тегам