Skip to content

Commit

Permalink
Clean up code
Browse files Browse the repository at this point in the history
  • Loading branch information
pawelmilek committed Dec 13, 2023
1 parent 45893e3 commit e32c882
Show file tree
Hide file tree
Showing 23 changed files with 343 additions and 272 deletions.
68 changes: 68 additions & 0 deletions SwiftyForecast Widget/Provider/WeatherProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// WeatherProvider.swift
// SwiftyForecast
//
// Created by Pawel Milek on 12/8/23.
// Copyright © 2023 Pawel Milek. All rights reserved.
//

import WidgetKit
import SwiftUI
import CoreLocation

struct WeatherProvider: TimelineProvider {
private let locationManager = WidgetLocationManager()
private let dataSource: WeatherProviderDataSource

init(dataSource: WeatherProviderDataSource = WeatherProviderDataSource()) {
self.dataSource = dataSource
}

func placeholder(in context: Context) -> WeatherEntry {
WeatherEntry.sampleTimeline.first!
}

func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> Void) {
Task {
let result = await loadWeatherDataForCurrentLocation()
let now = Date.now

let entry = WeatherEntry(
date: now,
locationName: result.name,
icon: result.icon,
description: result.description,
temperature: result.temperature,
temperatureMaxMin: result.temperatureMaxMin
)

completion(entry)
}
}

func getTimeline(in context: Context, completion: @escaping (Timeline<WeatherEntry>) -> Void) {
Task {
let result = await loadWeatherDataForCurrentLocation()
let now = Date.now

let entry = WeatherEntry(
date: now,
locationName: result.name,
icon: result.icon,
description: result.description,
temperature: result.temperature,
temperatureMaxMin: result.temperatureMaxMin
)

let nextUpdate = Calendar.current.date(byAdding: .minute, value: 45, to: now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}

private func loadWeatherDataForCurrentLocation() async -> WeatherProviderDataSource.EntryData {
let location = await locationManager.startUpdatingLocation()
let result = await dataSource.loadEntryData(for: location)
return result
}
}
84 changes: 84 additions & 0 deletions SwiftyForecast Widget/Provider/WeatherProviderDataSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// WeatherProviderDataSource.swift
// SwiftyForecastWidgetExtension
//
// Created by Pawel Milek on 12/13/23.
// Copyright © 2023 Pawel Milek. All rights reserved.
//

import Foundation
import SwiftUI
import CoreLocation

struct WeatherProviderDataSource {
struct EntryData {
let name: String
let icon: Image
let description: String
let temperature: String
let temperatureMaxMin: String
}

private let service: WeatherServiceProtocol
private let temperatureRenderer: TemperatureRenderer

init(
service: WeatherServiceProtocol = WeatherService(),
temperatureRenderer: TemperatureRenderer = TemperatureRenderer()
) {
self.service = service
self.temperatureRenderer = temperatureRenderer
}

func loadEntryData(for location: CLLocation) async -> EntryData {
let placemark = await fetchPlacemark(at: location)
let name = placemark.locality ?? InvalidReference.undefined
let coordinate = placemark.location?.coordinate ?? location.coordinate
let model = await fetchCurrentWeather(coordinate: coordinate)
let icon = await fetchIcon(with: model.icon)

let readyForDisplayTemperature = temperatureRenderer.render(model.temperatureValue)

let result = EntryData(
name: name,
icon: icon,
description: model.description,
temperature: readyForDisplayTemperature.current,
temperatureMaxMin: readyForDisplayTemperature.maxMin
)

return result
}

private func fetchPlacemark(at location: CLLocation) async -> CLPlacemark {
do {
let placemark = try await Geocoder.fetchPlacemark(at: location)
return placemark
} catch {
fatalError(error.localizedDescription)
}
}

private func fetchCurrentWeather(coordinate: CLLocationCoordinate2D) async -> CurrentWeatherModel {
do {
let response = try await service.fetchCurrent(
latitude: coordinate.latitude,
longitude: coordinate.longitude
)
let model = ResponseParser.parse(current: response)
return model
} catch {
fatalError(error.localizedDescription)
}
}

private func fetchIcon(with symbol: String) async -> Image {
do {
let result = try await service.fetchLargeIcon(symbol: symbol)
let image = Image(uiImage: result)
return image
} catch {
fatalError(error.localizedDescription)
}
}
}
26 changes: 13 additions & 13 deletions SwiftyForecast Widget/WeatherEntry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,46 @@ import SwiftUI

struct WeatherEntry: TimelineEntry {
let date: Date
let locationName: String
let icon: Image
let description: String
let temperature: String
let temperatureMaxMin: String
let locationName: String
let description: String

init(
date: Date,
locationName: String,
icon: Image,
description: String,
temperature: String,
temperatureMaxMin: String,
locationName: String,
description: String
temperatureMaxMin: String
) {
self.date = date
self.locationName = locationName
self.icon = icon
self.description = description
self.temperature = temperature
self.temperatureMaxMin = temperatureMaxMin
self.locationName = locationName
self.description = description
}
}

extension WeatherEntry {
static let sampleTimeline = [
WeatherEntry(
date: Date(),
locationName: "Cupertino",
icon: Image(.cloudySky),
description: "light intensity shower rain",
temperature: "69°",
temperatureMaxMin: "⏶ 75° ⏷ 72°",
locationName: "Cupertino",
description: "light intensity shower rain"
temperatureMaxMin: "⏶ 75° ⏷ 72°"
),
WeatherEntry(
date: Date(),
locationName: "Cupertino",
icon: Image(.clearSky),
description: "scattered clouds",
temperature: "87°",
temperatureMaxMin: "⏶ 92° ⏷ 45°",
locationName: "Cupertino",
description: "scattered clouds"
temperatureMaxMin: "⏶ 92° ⏷ 45°"
)
]
}
141 changes: 0 additions & 141 deletions SwiftyForecast Widget/WeatherProvider.swift

This file was deleted.

Loading

0 comments on commit e32c882

Please sign in to comment.