I am attempting to plot on a lineMark chart the ambient temperature for 10-days prior to the current date. Currently my LineMark chart is show the current date and a 10-day forecast out(For example: Todays date is March 14th so the chart show up until March 24th. But I want the chart to show March 4th to today's current date March 14th). I am new to using WeatherKit so I am not sure this is even possible. I have tried to add an extension to get the previous 10-days but then it starts to just show one point for one day on the chart instead of the full chart. This code provided below, shows the 10-day forecast for ambient temperature from March 14th to March 24th.
import SwiftUI
import CoreLocation
import WeatherKit
import Charts
import Foundation
enum ForecastType: String, CaseIterable, Identifiable {
case hourly = "Hourly Forecast"
case daily = "10-Day Forecast"
var id: String { self.rawValue }
}
@MainActor
class WeatherData: ObservableObject {
static let shared = WeatherData()
private let service = WeatherService.shared
@Published var currentWeather: CurrentWeather?
@Published var hourlyForecast: [HourWeather] = []
@Published var dailyForecast: [DayWeather] = []
func updateCurrentWeather(userLocation: CLLocation) {
Task.detached(priority: .userInitiated) {
do {
let forecast = try await self.service.weather(
for: userLocation,
including: .current)
DispatchQueue.main.async {
self.currentWeather = forecast
}
} catch {
print(error.localizedDescription)
}
}
}
func updateHourlyWeather(userLocation: CLLocation) {
Task.detached(priority: .userInitiated) {
do {
let forecast = try await self.service.weather(
for: userLocation,
including: .hourly)
DispatchQueue.main.async {
self.hourlyForecast = forecast.forecast.sorted(by: { $0.date < $1.date })
}
} catch {
print(error.localizedDescription)
}
}
}
func updateDailyWeather(userLocation: CLLocation) {
Task.detached(priority: .userInitiated) {
do {
let forecast = try await self.service.weather(for: userLocation, including: .daily)
DispatchQueue.main.async {
self.dailyForecast = forecast.forecast
}
} catch {
print("Failed to fetch daily weather data: \(error.localizedDescription)")
}
}
}
}
struct DailyForecastChartView: View {
var dailyForecast: [DayWeather]
var body: some View {
Chart {
ForEach(dailyForecast, id: \.date) { day in
let averageTemp = (day.highTemperature.value + day.lowTemperature.value) / 2
LineMark(
x: .value("Day", day.date, unit: .day),
y: .value("High Temperature", averageTemp)
)
.foregroundStyle(.green)
}
.symbol(Circle().strokeBorder())
.interpolationMethod(.catmullRom)
}
.chartXAxis {
AxisMarks(preset: .aligned, position: .bottom)
}
.chartYAxis {
AxisMarks(preset: .aligned, position: .leading)
}
}
}
struct LocalizedWeatherView: View {
@ObservedObject var weatherDataHelper = WeatherData.shared
@ObservedObject var userLocationHelper = WeatherLocationManager.shared
@State private var selectedDate: Date = Date()
@State private var selectedForecastType: ForecastType = .hourly
var body: some View {
Form {
if let currentWeather = weatherDataHelper.currentWeather {
Section {
Label(currentWeather.temperature.formatted(), systemImage: "thermometer")
Label("\(Int(currentWeather.humidity * 100))%", systemImage: "humidity.fill")
if userLocationHelper.userLocation == nil {
Button("Load user current location") {
loadUserCurrentLocation()
}
}
Button("Fetch current weather") {
loadCurrentWeatherData()
}
}
}
Section(header: Text(selectedForecastType == .hourly ? "" : "")) {
Picker("Forecast Type", selection: $selectedForecastType) {
ForEach(ForecastType.allCases) { type in
Text(type.rawValue).tag(type)
}
}
.pickerStyle(SegmentedPickerStyle())
if selectedForecastType == .hourly {
if !weatherDataHelper.hourlyForecast.isEmpty {
HourlyForecastChartView(hourlyForecast: weatherDataHelper.hourlyForecast)
.frame(height: 300)
} else {
Text("No hourly forecast data available.")
}
} else if selectedForecastType == .daily {
if !weatherDataHelper.dailyForecast.isEmpty {
DailyForecastChartView(dailyForecast: weatherDataHelper.dailyForecast)
.frame(height: 300)
} else {
Text("Loading daily forecast data")
}
}
}
}
.navigationTitle("Localized Weather")
.onAppear {
loadAllWeatherData()
}
}
func loadAllWeatherData() {
guard let userLocation = userLocationHelper.userLocation else {
return
}
weatherDataHelper.updateCurrentWeather(userLocation: userLocation)
weatherDataHelper.updateHourlyWeather(userLocation: userLocation)
weatherDataHelper.updateDailyWeather(userLocation: userLocation)
}
func loadUserCurrentLocation() {
userLocationHelper.requestPermission()
userLocationHelper.locationManager.requestLocation()
}
func loadCurrentWeatherData() {
guard let userLocation = WeatherLocationManager.shared.userLocation else {
return
}
Task.detached { @MainActor in
weatherDataHelper.updateCurrentWeather(userLocation: userLocation)
}
}
}
extension WeatherData {
var last10DaysForecast: [DayWeather] {
let calendar = Calendar.current
guard let tenDaysAgo = calendar.date(byAdding: .day, value: -10, to: Date()) else { return [] }
return dailyForecast.filter {
let isBeforeToday = $0.date < Date()
let isAfterTenDaysAgo = $0.date >= tenDaysAgo
return isBeforeToday && isAfterTenDaysAgo
}
.sorted { $0.date < $1.date }
}
}