Swift URLSession and Conbine json array decode fails

Viewed 29

Well guys like the title says i have on a response service call an JSON array and i cant find the way to decode with Combine: URLSession.shared.dataTaskPublisher

Service Response: https://codebeautify.org/alleditor/y228809f7

My Request Code:

public func getGasStationDiscounts(requestModel: GasStationDiscountsRequestDomainModel) -> CiMAObservable<GasStationDiscountsDomainModel> {
    guard let url = URL(string: RepositoryConstants.baseURL + String(format: RepositoryConstants.EndPoints.gasStationDiscounts, requestModel.gasStationID)) else {
        return Fail(error: NSError(domain: "URL Invalid", code: 001, userInfo: nil)).eraseToAnyPublisher()
    }
    
    var urlRequest = URLRequest(url: url)
    urlRequest.httpMethod = "GET"
    urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")
    urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
    
    return URLSession.shared.dataTaskPublisher(for: urlRequest)
        .map(\.data)
        .decode(type: GasStationDiscountsDataModel.self, decoder: JSONDecoder())
        .map { model -> GasStationDiscountsDomainModel in
            model.parseToDomainModel()
        }.eraseToAnyPublisher()
}

Decodable File: https://codebeautify.org/alleditor/y2296aefe

So resuming, my problem is on trying to decode it because its an Array, on my project i'm working with diferent layers like Data, Domain and the Presentation (don't ask about architecture cuz is a new one, CiMA on GitHub) its a Hybrid Viper arch.

Thanks in advance!

EDITED: SOLVED! So finally i found a Solution which don't destroy my architecture. So here the small changes on decodable Files: https://codebeautify.org/alleditor/y22ad811e

and the last changes on the Request:

    public func getGasStationDiscounts(requestModel: GasStationDiscountsRequestDomainModel) -> CiMAObservable<GasStationDiscountsDomainModel> {
    guard let url = URL(string: RepositoryConstants.baseURL + String(format: RepositoryConstants.EndPoints.gasStationDiscounts, requestModel.gasStationID)) else {
        return Fail(error: NSError(domain: "URL Invalid", code: 001, userInfo: nil)).eraseToAnyPublisher()
    }
    
    var urlRequest = URLRequest(url: url)
    urlRequest.httpMethod = "GET"
    urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")
    urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
    
    return URLSession.shared.dataTaskPublisher(for: urlRequest)
        .map(\.data)
        .decode(type: [GasStationDiscountsDataModel].self, decoder: JSONDecoder())
        .map { model -> GasStationDiscountsDomainModel in
            let stationDiscountsDomainModel = model.map { model -> StationDiscountDomainModel in
                model.parseToDomainModel()
            }
            return GasStationDiscountsDomainModel(stationDiscounts: stationDiscountsDomainModel)
        }
        .mapError { error in
            print(error.localizedDescription)
            return error
        }.eraseToAnyPublisher()
}
2 Answers

The response seems to be an array. There is no top level element. Use:

.decode(type: [StationDiscountDataModel].self, decoder: JSONDecoder())

Create custom operator something like this:

 extension Publisher where Output == Data {
    func customOperator() -> AnyPublisher<GasStationDiscountsDataModel, Never>  {
       return Just(GasStationDiscountsDataModel(data: self as! Data))
              .eraseToAnyPublisher()
       }
 }

And then use it:

 return URLSession.shared.dataTaskPublisher(for: urlRequest)
    .map(\.data)
    .customOperator()
    .map { model -> GasStationDiscountsDomainModel in
        model.parseToDomainModel()
    }.eraseToAnyPublisher()
Related