SwiftUI实现天气条件变化实时更新视图及定时更新方案咨询
实现天气数据的定时更新与实时更新
一、定时更新方案
方法1:基于定时器(Timer)实现
在WeatherKitManager中添加定时器管理逻辑,控制定时拉取天气数据:
@MainActor class WeatherKitManager: ObservableObject { @Published var weather: Weather? @Published var state: LoadingState = .idle @Published var condition: WeatherCondition = .clear private var updateTimer: Timer.TimerPublisher? private var cancellables = Set<AnyCancellable>() enum LoadingState { case idle, loading, success(Weather), failure(Error) } // 启动定时更新,默认5分钟一次(可自定义间隔) func startAutoUpdate(interval: TimeInterval = 300) { stopAutoUpdate() updateTimer = Timer.publish(every: interval, on: .main, in: .common) updateTimer? .autoconnect() .sink { [weak self] _ in Task { await self?.getWeather() } } .store(in: &cancellables) } // 停止定时更新 func stopAutoUpdate() { updateTimer?.upstream.connect().cancel() cancellables.removeAll() } func getWeather() async { state = .loading do { let weather = try await WeatherService.shared.weather(for: .init(latitude: 42.34779, longitude: 13.39943)) state = .success(weather) condition = weather.currentWeather.condition } catch { state = .failure(error) } } deinit { stopAutoUpdate() } }
在ContentView的初始化任务中启动定时更新:
.task { await weatherKitManager.getWeather() weatherKitManager.startAutoUpdate(interval: 300) // 启动5分钟一次的更新 }
方法2:基于Swift并发异步循环实现
用异步循环替代定时器,更符合Swift并发编程范式:
@MainActor class WeatherKitManager: ObservableObject { @Published var weather: Weather? @Published var state: LoadingState = .idle @Published var condition: WeatherCondition = .clear private var updateTask: Task<Void, Never>? enum LoadingState { case idle, loading, success(Weather), failure(Error) } func startAutoUpdate(interval: TimeInterval = 300) { stopAutoUpdate() updateTask = Task { [weak self] in while !Task.isCancelled { await self?.getWeather() do { try await Task.sleep(nanoseconds: UInt64(interval * 1_000_000_000)) } catch { break } } } } func stopAutoUpdate() { updateTask?.cancel() updateTask = nil } func getWeather() async { state = .loading do { let weather = try await WeatherService.shared.weather(for: .init(latitude: 42.34779, longitude: 13.39943)) state = .success(weather) condition = weather.currentWeather.condition } catch { state = .failure(error) } } deinit { stopAutoUpdate() } }
同样在ContentView的.task中调用startAutoUpdate即可。
二、实时更新方案(利用WeatherKit订阅能力)
WeatherKit提供原生订阅接口,仅在天气数据实际变化时触发更新,无需定时轮询:
@MainActor class WeatherKitManager: ObservableObject { @Published var weather: Weather? @Published var state: LoadingState = .idle @Published var condition: WeatherCondition = .clear private var weatherSubscription: AnyCancellable? enum LoadingState { case idle, loading, success(Weather), failure(Error) } // 启动实时天气订阅 func startRealTimeUpdate() { let location = CLLocationCoordinate2D(latitude: 42.34779, longitude: 13.39943) weatherSubscription = WeatherService.shared .subscribe(to: location) .sink(receiveCompletion: { [weak self] completion in if case .failure(let error) = completion { self?.state = .failure(error) } }, receiveValue: { [weak self] weather in self?.state = .success(weather) self?.condition = weather.currentWeather.condition self?.weather = weather }) } // 停止实时订阅 func stopRealTimeUpdate() { weatherSubscription?.cancel() weatherSubscription = nil } // 初始数据获取 func getWeather() async { state = .loading do { let weather = try await WeatherService.shared.weather(for: .init(latitude: 42.34779, longitude: 13.39943)) state = .success(weather) condition = weather.currentWeather.condition weather = weather } catch { state = .failure(error) } } deinit { stopRealTimeUpdate() } }
在ContentView中启动订阅:
.task { await weatherKitManager.getWeather() // 先获取初始数据 weatherKitManager.startRealTimeUpdate() }
注意事项
- 定时更新间隔建议不短于5分钟,避免触发WeatherKit的请求频率限制。
- 实时订阅更高效,仅在天气数据变化时更新,适合对实时性要求高的场景。
- 务必在视图销毁或Manager释放时停止更新/订阅,避免内存泄漏。
内容的提问来源于stack exchange,提问作者helloimbrando
相关产品推荐
相关产品推荐

