Last active
April 2, 2021 20:58
-
-
Save LeTadas/3687521631cca89ef3b68255b6b8fe66 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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