我正在摆弄苹果新推出的WeatherKit + WidgetKit,可惜我似乎找不到解决以下三个问题的方法:
- WidgetKit要求加载
getTimeline
函数中的所有数据(UI无动态更新)
1.加载时,WidgetKit出于某种原因调用getTimeline两次或更多次(Developer Forums)
1.我希望将WeatherKit请求保持在最低限度
上面的前两个问题迫使我在一个我无法控制的函数(getTimeline)中获取天气数据。
我的获取天气的函数已经缓存了Weather
对象,并确保只有该高速缓存太旧时才请求新数据。
private func getWeather() async -> Weather? {
// if cachedWeather is not older than 2 hours return it instead of fetching new data
if let cachedWeather = self.cachedWeather,
cachedWeather.currentWeather.date > Date().addingTimeInterval(-7200) {
return cachedWeather
}
return try? await Task { () -> Weather in
let fetchedWeather = try await WeatherService.shared.weather(for: self.location)
cachedWeather = fetchedWeather
return fetchedWeather
}.value
}
如果我从getTimeline
中调用getWeather()
,它可能会在几乎相同的时间被调用两次或更多次。只要第一个任务还没有完成,cachedWeather就仍然是空的/过时的。这会导致任务的多次执行,这反过来意味着向苹果发送了多个请求。
在应用的普通SwiftUI视图中,我会使用ObservableObject之类的东西,只有在getWeather()
中没有正在运行的东西时才触发请求。UI将基于ObservableObject更新。在WidgetKit中,如上所述,这是不可能的。
**问题:**有人能帮我弄清楚如何在第一次调用时触发getWeather()
中的任务,以及如果在第二次getWeather()
调用到来时任务已经/仍在运行,则使用已经运行的任务而不是触发新的任务吗?
1条答案
按热度按时间5uzkadbs1#
如果我没理解错的话,这就是演员的职责。试试这个:
正如您将看到的,计时器尝试每秒启动一次任务,但是如果任务正在运行,则尝试被退回;你只能在任务还没有运行的时候启动它。actor使这一切变得非常安全和线程一致。
关于你的评论:
缺少的是,如果timeConsumingThing在运行时被调用,我最终仍然需要结果...理想情况下,第二次调用将只是“订阅”同一个正在运行的异步任务。
我想我们可以通过添加一个实际的发布和订阅来模拟这一点。首先,让我分离出实际的任务并使其返回一个结果;这应该是你的WeatherKit交互
现在,我将稍微修改actor,以便新调用者必须等待最新WeatherKit交互返回的下一个结果:
最后,测试台与以前基本相同,但现在我将获得每次触发计时器时所做调用的结果,并在获得结果时打印它:
结果是:
正如您所看到的,每秒都有人调用我们的actor。每个调用者最终都会得到一个结果,而且它们都是“相同”的结果
2022-08-28 15:35:45
,因为这是耗时任务返回的时间。从那时起,最近的调用者都开始得到2022-08-28 15:35:50
,因为这是“下一个”耗时任务返回的时间。如我前面的示例所示,它被选通,因此只有在从上一次调用返回后才能被调用。