import SwiftUI import Combine enum URLSessionError: Error { case invalidResponse case serverErrorMessage(statusCode: Int, data: Data) case urlError(URLError) } extension URLSession { func send(request: URLRequest) -> AnyPublisher { dataTaskPublisher(for: request) .mapError { URLSessionError.urlError($0) } .flatMap { data, response -> AnyPublisher in guard let response = response as? HTTPURLResponse else { return .fail(.invalidResponse) } guard 200..<300 ~= response.statusCode else { return .fail(.serverErrorMessage(statusCode: response.statusCode, data: data)) } return .just(data) }.eraseToAnyPublisher() } } extension JSONDecoder: TopLevelDecoder {} extension Publisher { /// - seealso: https://twitter.com/peres/status/1136132104020881408 func flatMapLatest(_ transform: @escaping (Self.Output) -> T) -> Publishers.SwitchToLatest> where T.Failure == Self.Failure { map(transform).switchToLatest() } } extension Publisher { static func empty() -> AnyPublisher { return Empty() .eraseToAnyPublisher() } static func just(_ output: Output) -> AnyPublisher { return Just(output) .catch { _ in AnyPublisher.empty() } .eraseToAnyPublisher() } static func fail(_ error: Failure) -> AnyPublisher { return Fail(error: error) .eraseToAnyPublisher() } }