I'm using WeatherKit in a project to display the SF Symbol for the weather in the app toolbar. But when I build and run the app on my device, Weather
is always nil
.
This is the WeatherManager
class:
import Foundation
import WeatherKit
@MainActor class WeatherManager: ObservableObject {
@Published var weather: Weather?
public func getWeather() async {
do {
weather = try await Task.detached(priority: .userInitiated) {
return try await WeatherService.shared.weather(for: .init(latitude: 28.3772, longitude: -81.5707))
}.value
} catch {
fatalError("\(error)")
}
}
var symbol: String {
weather?.currentWeather.symbolName ?? "xmark.icloud.fill"
}
var temp: String {
let temp =
weather?.currentWeather.temperature
let convert = temp?.converted(to: .fahrenheit).description
return convert ?? ""
}
}
And this how I use it in the SwiftUI View:
ToolbarItem(placement: .topBarLeading) {
@ObservedObject var weatherManager = WeatherManager()
Image(systemName: weatherManager.symbol)
.task {
await weatherManager.getWeather()
}
}
The issue:
weatherManager.getWeather()
.weatherManager.symbol
toImage
before actual initialization ofweather
after the async task.Fix: You could do one of the following:
Image
:ObservableObject
, with a published variableweather
. Use it.Conclusion: When dealing with async tasks that return data, always make sure to handle the actual returned data. In your case, no data is handled after
await getWeather()
, and the symbol calculated property is never calculated after the first call (most probably, before the async task has finished). Also, make sure you use the correct property wrappers and learn the difference between@StateObject
and@ObservedObject
. This is a good guide.