I was reading this article: https://www.raywenderlich.com/4161005-mvvm-with-combine-tutorial-for-ios on Ray Wenderlich about how to use combine. They have an example where it fetches data from an API but it doesn't handle HTTP status codes. I wanted to add it but so far I'm not able to do so.
According to this answer you could add a tryMap but then XCode starts showing errors like: Generic parameter 'T' could not be inferred.
Below the code:
extension WeatherFetcher: WeatherFetchable {
func weeklyWeatherForecast(
forCity city: String
) -> AnyPublisher<WeeklyForecastResponse, WeatherError> {
return forecast(with: makeWeeklyForecastComponents(withCity: city))
}
private func forecast<T>(
with components: URLComponents
) -> AnyPublisher<T, WeatherError> where T: Decodable {
guard let url = components.url else {
let error = WeatherError.network(description: "Couldn't create URL")
return Fail(error: error).eraseToAnyPublisher()
}
return session.dataTaskPublisher(for: URLRequest(url: url))
.mapError { error in
.network(description: error.localizedDescription)
}
.flatMap(maxPublishers: .max(1)) { pair in
decode(pair.data)
}
.eraseToAnyPublisher()
}
}
And I was trying to add
.tryMap { data, response in
guard let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode else {
switch (response as! HTTPURLResponse).statusCode {
case (400...499):
throw ServiceErrors.internalError((response as! HTTPURLResponse).statusCode)
default:
throw ServiceErrors.serverError((response as! HTTPURLResponse).statusCode)
}
}
return data
}
I think you can simply replace the flatMap block with tryMap. And instead of returning data from tryMap, it should be decoded T. So return data line should be return try JSONDecoder().decode(T.self, from: data)
private func forecast<T>(with components: URLComponents) -> AnyPublisher<T, WeatherError> where T: Decodable {
guard let url = components.url else {
let error = WeatherError.network(description: "Couldn't create URL")
return Fail(error: error).eraseToAnyPublisher()
}
return session.dataTaskPublisher(for: URLRequest(url: url))
.tryMap { data, response in
guard let httpResponse = response as? HTTPURLResponse,
200..<300 ~= httpResponse.statusCode else {
switch (response as! HTTPURLResponse).statusCode {
case (400...499):
throw ServiceErrors.internalError((response as! HTTPURLResponse).statusCode)
default:
throw ServiceErrors.serverError((response as! HTTPURLResponse).statusCode)
}
}
return try JSONDecoder().decode(T.self, from: data)
}
.mapError { error in
WeatherError() // some kind of error
}
.eraseToAnyPublisher()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With