Skip to content

Instantly share code, notes, and snippets.

@LeTadas
Last active April 2, 2021 20:58
Show Gist options
  • Save LeTadas/3687521631cca89ef3b68255b6b8fe66 to your computer and use it in GitHub Desktop.
Save LeTadas/3687521631cca89ef3b68255b6b8fe66 to your computer and use it in GitHub Desktop.
import Combine
import Foundation
enum RequestError: Error {
case urlError(URLError)
case decodingError(DecodingError)
case genericError(Error)
}
class NetworkClient {
func execute<T: Decodable>(url: URLRequest) -> AnyPublisher<Result<T, RequestError>, Never> {
return URLSession.shared.dataTaskPublisher(for: url)
.tryMap { element -> Data in
guard let httpResponse = element.response as? HTTPURLResponse,
httpResponse.statusCode == 200
else {
throw URLError(.badServerResponse)
}
return element.data
}
.decode(type: T.self, decoder: JSONDecoder())
.map {
.success($0)
}
.catch { error -> Just<Result<T, RequestError>> in
if let urlError = error as? URLError {
return Just(.failure(RequestError.urlError(urlError)))
} else if let decodingError = error as? DecodingError {
return Just(.failure(RequestError.decodingError(decodingError)))
} else {
return Just(.failure(RequestError.genericError(error)))
}
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
class TelemetryProvider {
private let networkClient: NetworkClient
init(_ networkClient: NetworkClient) {
self.networkClient = networkClient
}
func getTelemetry(treeId: Int) -> AnyPublisher<Result<[Telemetry], RequestError>, Never> {
let url = URL(string: "\(ApiConfig.url)/telemetry/\(treeId)")
guard let requestUrl = url else {
fatalError("Could not parse url TelemetryProvider")
}
let urlRequest = URLRequest(url: requestUrl)
return networkClient.execute(url: urlRequest)
.eraseToAnyPublisher()
}
}
class ImageProvider {
private let networkClient: NetworkClient
init(_ networkClient: NetworkClient) {
self.networkClient = networkClient
}
func getImages() -> AnyPublisher<Result<[Image], RequestError>, Never> {
let url = URL(string: "\(ApiConfig.url)/image")
guard let requestUrl = url else {
fatalError("Could not parse url ImageProvider")
}
let urlRequest = URLRequest(url: requestUrl)
return networkClient.execute(url: urlRequest)
.eraseToAnyPublisher()
}
}
class TimelineProvider {
private let imageProvider: ImageProvider
private let telemetryProvider: TelemetryProvider
init(
_ imageProvider: ImageProvider,
_ telemetryProvider: TelemetryProvider
) {
self.imageProvider = imageProvider
self.telemetryProvider = telemetryProvider
}
func getTimeline() -> AnyPublisher<Result<[TimelineItem], RequestError>, Never> {
Publishers.CombineLatest(
imageProvider.getImages(),
telemetryProvider.getTelemetry()
)
. map { [unowned self] (images, telemetry) in
self.mapToTimelineItem(images: images, telemetry: telemetry)
}
.eraseToAnyPublisher()
}
private func mapToTimelineItem(images: [Image], telemetry: [Telemetry]) -> [TimelineItem] {
// Do some sorting and mapping
}
}
// Item which you will display in view
enum TimelineItem {
case image(Image)
case health(Int)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment