I am trying to use WeatherKit in a SwiftUI app to get historical weather data for a range of dates. The info in this post: Can't Find WeatherKit Historical Data and Trouble with Attribution
Leads me to believe that the correct call is:
let forecast = try await weatherService.weather(for: location, including: .daily(startDate: startDate, endDate: endDate))
However, when I try this in my test app, it fails with the following error:
responseFailed(<NSHTTPURLResponse: 0x600000225be0> { URL: https://weather-data.apple.com/v3/weather/en-US/37.770/-122.410?timezone=America/Los_Angeles&dataSets=forecastDaily&dailyStart=2023-10-02T16:45:34Z&dailyEnd=2023-10-17T16:45:34Z&country=US&deviceLanguages=en-US&clientMetadata=CgJVUxoFZW4tVVNKAFABWAxgAIABA5gBALoBAhAB4gEICgseHR8hICLyAQJVUw } { Status Code: 404, Headers { "Access-Control-Allow-Origin" = ( "*" ); "Cache-Control" = ( "max-age=0, no-cache, no-store" ); Connection = ( "keep-alive" ); "Content-Length" = ( 0 ); "Content-Security-Policy" = ( "default-src 'self';" ); Date = ( "Sun, 22 Oct 2023 16:45:34 GMT" ); Expires = ( "Sun, 22 Oct 2023 16:45:34 GMT" ); Pragma = ( "no-cache" ); Server = ( Apple ); "Strict-Transport-Security" = ( "max-age=31536000; includeSubdomains" ); "X-Apple-Origin" = ( "8961098f-b238-3714-ba44-5b569c861456" ); "X-Cache" = ( "TCP_MISS from a23-204-105-90.deploy.akamaitechnologies.com (AkamaiGHost/11.3.0-51620215) (-)" ); "X-Content-Type-Options" = ( nosniff ); "X-Frame-Options" = ( SAMEORIGIN ); "X-REQUEST-ID" = ( "CB4405F3-9FF0-4E60-81A3-D9657E9BA100" ); "X-XSS-Protection" = ( "1; mode=block" ); } }, Optional(""))
Here's the code I have where I am making the call to WeatherKit:
import SwiftUI
import WeatherKit
import CoreLocation
struct ContentView: View {
let location = CLLocation(latitude: 37.77, longitude: -122.41)
let startDate = daysAgo(days: -20)
let endDate = daysAgo(days: -5)
let weatherService = WeatherService.shared
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
.task {
do {
//let weather = try await weatherService.weather(for: location)
let weather = try await weatherService.weather(for: location, including: .daily(startDate: startDate, endDate: endDate))
print(weather)
} catch {
print(error)
}
}
}
}
func daysAgo(days: Int) -> Date {
var dayComponent = DateComponents()
dayComponent.day = days
let calendar = Calendar.current
let prevDay = calendar.date(byAdding: dayComponent, to: Date())!
return prevDay
}
#Preview {
ContentView()
}
Note that I have all the capabilities provisioning done correctly in info.plist and on my developer account on apple.com. The above code works correctly and prints the current weather if I simply ask for current weather using this:
let weather = try await weatherService.weather(for: location)
Where am I going wrong here? Is there some other way I should be using WeatherKit to get historical data? I should also make it clear that my goal is to obtain a list of dates with daily minimum and maximum temperatures for the given location. So for this hard-coded example, I'm expecting to get back the daily high and low temperatures for the coded location from 20 days ago to 5 days ago.
UPDATE: I noticed in the WeatherKit documentation that it says "Each request will return a maximum of 10 days". I thought this would only apply to future forecasts, not historical data so I ignored it. Well, when I tried a date range that was less than 10 days I did get some data but not what I expected. For a date range of 8 days in the past, I got the data below which only contains info for the first and last day in the date range and no data for the days in between. Is this how this is supposed to work? It makes no sense. How do I get the min and max temperatures for all days in the date range?
days count=8, date range=2023-07-15 07:00:00 +0000 to 2023-07-22 07:00:00 +0000, first condition=Partly Cloudy, first high=22.64 °C, first low=12.13 °C, last condition=Partly Cloudy, last high=22.32 °C, last low=12.1 °C
Indeed, Apple restricts historical weather with 10 days limit. My workaround is that I split the job into 10 days chunks and loop through them to get the number of days needed. This may lead to quite a boilerplate code, but it works for me.