Can't Get Historical Weather Data using WeatherKit in SwiftUI App

149 views Asked by At

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

2

There are 2 answers

1
Serhii Pryimachuk On

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.

0
Orestes On

Ok, after a large amount of trial and error, I figured out why I was only seeing the data for start and end dates only (and not the days in between). Chalk this up to newbie inexperience. My problem is at the point where I print the entire object as I do above in the line "print(weather)". It appears that this prints a summary of what the variable weather contains and not the entire contents. That's why I don't see the data for dates in between the date range.

In order to actually access and see/use all the data you have to access each element in 'weather' in an array format. So, for example, if you want to access the low temperature on the fourth day of range of 7 days, you would simply do this:

let fourthLowTemp = weather[3].lowTemperature.value

Similarly for other elements in the data. Hope this helps other newbies who get stuck on stuff like this.