laneboyandrew
laneboyandrew

Reputation: 343

Decode response with headers and body using init from decoder

I'm having hard time trying to decode response. I need response body and one response header and I also need to customize my decoding process using LossyCodableList to make sure parsing will be successful even if some elements in response body are incorrect. My first solution is: I get response data with headers, then I map it into my response model using standard .init() and it works, but in this case my problem is that init from decoder has never been called hence I can't customize decoding process with LossyCodableList.

func programs(request: CardsRequest) -> AnyPublisher<CardListResponse, Error> {
        apiClient.request(.programs(request)).map {
            let cardListItem: CardListResponse =
                .init(
                    totalProgramsNumber: $0.headers?["total"] as? String ?? "0",
                    cardsData: $0.data
                )
            return cardListItem
        }
        .eraseToAnyPublisher()
    }

struct CardListResponse: Decodable {
    var totalProgramsNumber: String?
    let cardsData: [CardListItem]
    
    enum CodingKeys: String, CodingKey {
        case totalProgramsNumber
        case cardsData
    }
    
    init(totalProgramsNumber: String? = "0", cardsData: [CardListItem]) {
       self.totalProgramsNumber = totalProgramsNumber
       self.cardsData = cardsData
   }
    
    init(from decoder: any Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.totalProgramsNumber = try? container.decode(String.self, forKey: .totalProgramsNumber)
        
        do {
            let elements = try container.decode(LossyCodableList<CardListItem>.self, forKey: .cardsData)
            cardsData = elements.elements
        } catch {
            cardsData = []
        }
    }
}

I also tried another solution: I removed standard .init() and now init from decoder has been called but I don't understand how to get and use data from headers in structure CardListResponse.

func programs(request: CardsRequest) -> AnyPublisher<CardListResponse, Error> {
   apiClient.request(.programs(request)).map { $0.data }
      .eraseToAnyPublisher()
}

struct CardListResponse: Decodable {
    var totalProgramsNumber: String?
    let cardsData: [CardListItem]
    
    enum CodingKeys: String, CodingKey {
        case totalProgramsNumber
        case cardsData
    }
    
    init(from decoder: any Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.totalProgramsNumber = try? container.decode(String.self, forKey: .totalProgramsNumber)
        
        do {
            let elements = try container.decode(LossyCodableList<CardListItem>.self, forKey: .cardsData)
            cardsData = elements.elements
        } catch {
            cardsData = []
        }
    }
}

Any advice would be appreciated.

Upvotes: 0

Views: 43

Answers (0)

Related Questions