My app is a smart home controller and makes decisions based on local weather. Users have been seeing absurdly wrong (10°F off) current temperatures, and of course that's a problem. I've seen similar complaints on Reddit and one poster claims that Apple switched from whatever DarkSky was using (which seemed accurate) to a predictive model from IBM. Compare iOS15 to iOS16 and you may see large discrepancy in the Weather app.
Post
Replies
Boosts
Views
Activity
I have succeeded in fetching weather data during an app refresh in the background! So it is indeed possible and it's been working great. My app already had background app refresh set up to launch a URL session, and I no longer need that now that WeatherKit can fetch the data I need.
My question now, before I push my new version out to the store, is how to handle any errors that WeatherKit might throw at me. I don't want the app to crash if the service goes down or whatever. The documentation describes a WeatherError but I'm confused over how to implement that. Need an example
If you're still looking for a solution, try this: Sign into your developer account. Choose "Certificates, IDs and Profiles", then choose "Identifiers". Your sample app and identifier should appear on the list. Select it. On the screen that comes up you'll notice there are two tabs, Capabilities and App Services. WeatherKit must be checked ON BOTH tabs, and you must hit the SAVE button in the upper right for the checkmark to "stick".
Same. How do you Simulate a Background Fetch like we used to?
MacOS 14.2.1 Xcode 15.2
Current Simulator device is iOS 17.2 iPhone 15 Pro Max
I did it! The key learning for me was to "include .minute" in the weather query and to then receive back an array of minute-by-minute data. You can then step thru the forecast array and pick out the data you need, in my case the precipitationChance.
Successful code:
import Foundation
import WeatherKit
@Observable class WeatherManager {
private let weatherService = WeatherService()
var weather: CurrentWeather?
var forcast: Forecast<MinuteWeather>?
var pop: [Double] = [] // Will build this array with precipitationChance for each minute forward
func getWeather(lat: Double, long: Double) async {
do {
let weatherFetch = try await weatherService.weather(for: .init(latitude: lat, longitude: long), including: .current, .minute)
weather = weatherFetch.0 // The current weather
forcast = weatherFetch.1 // The forecast by minute for the next hour or so
var i: Int = -1
forcast?.forEach { weatherEntry in // This loop loads the precipitationChance values into the pop array
i = i + 1
let timer = DateFormatter.localizedString(from: weatherEntry.date, dateStyle: .short, timeStyle: .short)
pop.append(weatherEntry.precipitationChance)
print("Time \(i): \(timer) = \(pop[i])") // Time and printing are for debugging
}
} catch let error {
print("Failed to get the weather and/or forecast. \(error)")
}
} // close getWeather function
var icon: String {
guard let iconName = weather?.symbolName else { return "--" }
return iconName
}
var temperature: String {
guard let temp = weather?.temperature else { return "--" }
let convert = temp.converted(to: .fahrenheit).value
return String(Int(convert)) + "°F"
}
var humidity: String {
guard let humidity = weather?.humidity else { return "--" }
let computedHumidity = humidity * 100
return String(Int(computedHumidity)) + "%"
}
var pressure: String {
guard let press = weather?.pressure else { return "--" }
let convertP = press.converted(to: UnitPressure.inchesOfMercury).value
return String((convertP)) + " in. Hg"
}
var UVindex: String {
guard let uv = weather?.uvIndex.value else { return "--" }
return "\(uv)" + " index"
}
var POP: String {
if pop.count > 11 { // Check that there are at least 11 minutes of forecast data
var chance = pop[10] * 100.0 // Report the forecast for the (arbitrary) 11th entry
return String(Int(chance)) + "%"
} else {
return "--"
}
}
}
Tests complete. I can successfully use the FlightPlanner demo or a simpler project from here:
https://github.com/coledennis/WeatherKit_CoreLocation_SwiftUI_Tutorial
... (before and) after changing the bundle ID to my own, one which I have registered for WeatherKit. This was not the case when I started, though.
But, a playground with any of the code samples above still throws the same errors as before.
I can abandon trying to use Playground to learn my way around WeatherKit. I'll switch to using demo apps. I thought it would make things easier and faster to use a Playground but that's obviously not the case.
Another clue? Following the example at https://blog.makwanbk.com/weatherkit-build-a-simple-ios-weather-app, I put the following code in my playground and got the error below. It's pretty much the same as before but now expressed in a localized description.
I was looking at my developer account and discovered I have no current provisioning profiles. Would that do it? As I said the Airport Forecast example app works in Simulator
import UIKit
import WeatherKit
import CoreLocation
let service = WeatherService()
let currentLocation = CLLocation(latitude: 37.7749, longitude: 122.4194)
do {
let weather = try await service.weather(for: currentLocation)
print(weather)
let currentWeather = Weather(temperature: weather.currentWeather.temperature.value,
condition: weather.currentWeather.condition.rawValue,
symbolName: weather.currentWeather.symbolName,
humidity: weather.currentWeather.humidity,
isDaylight: weather.currentWeather.isDaylight)
print(currentWeather)
} catch {
assertionFailure(error.localizedDescription)
}
struct Weather {
let temperature: Double
let condition: String
let symbolName: String
let humidity: Double
let isDaylight: Bool
static func empty() -> Weather {
Weather(temperature: 0,
condition: "",
symbolName: "",
humidity: 0,
isDaylight: false)
}
}
__lldb_expr_17/Weather.playground:31: Fatal error: The operation couldn’t be completed. (WeatherDaemon.WDSJWTAuthenticatorServiceProxy.Errors error 0.)
I'm having a harder time trying that than you'd expect. Getting it to work in an app was what I was trying to avoid by using the playground to get my code working first.
Does your question/answer imply that it SHOULD be working in a playground? I mean, does that code work for you?
Update: This code does not crash the app, and the error returned hints that I might have an access issue. I'm still wondering if this is even possible in a playground.
As an aside, I have successfully used Apple's Flight Planner app that is offered as a WeatherKit demo. So my account seems to be "authorized" or whatever the word is.
import UIKit
import WeatherKit
import CoreLocation
let sanFrancisco = CLLocation(latitude: 37.7749, longitude: 122.4194)
do {
let weather = try await WeatherService().weather(for: sanFrancisco)
} catch {
print(error)
}
This produces the error:
xpcConnectionFailed(Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.weatherkit.authservice" UserInfo={NSDebugDescription=connection to service named com.apple.weatherkit.authservice})
Playground execution failed:
error: execution stopped with unexpected state.
error: Execution was interrupted.
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
Does that code not crash your Xcode? Again, it's in a playground.
This is still an open question. @naticio, your reply suggests that it cannot run in the background. Am I misreading you? The inability to check the weather occasionally without user input would be a HUGE negative for WeatherKit. It means you can't "warn" the user of significant weather changes like imminent rain.
Related question: Is it possible to play with weatherKit in an Xcode playground? So far I can reliably crash Xcode by even attempting this. I'll post this as a separate question.
I found another observation of the problem here: https://github.com/react-native-picker/picker/issues/212
Indeed. If you can attach two builds of your app, one with Info.plist string and one without, that’d be grand. I've been traveling and only now saw your reply.
The working version is now available in the app store (as RIDS calc"). I'll be happy to provide a build with the string removed if you can detail what you mean by "attach".
Still a problem. Xcode Version 12.0 (12A7209)
Per advice here, I deleted the iOS 14 folder inside ~/Library/Developer/Xcode/iOS DeviceSupport.
(My iPhone is now on 14.0.1, so I left the 13.7 folder alone.)
It took a long time for Xcode to "copy cache files" from the iPhone but once that finally got done, normal build-and-run behavior is back. Nice and snappy.
Still a problem. Xcode Version 12.0 (12A7209)
Per advice here, I deleted the iOS 14 folder inside ~/Library/Developer/Xcode/iOS DeviceSupport.
(My iPhone is now on 14.0.1, so I left the 13.7 folder alone.)
It took a long time for Xcode to "copy cache files" from the iPhone but once that finally got done, normal build-and-run behavior is back. Nice and snappy.