You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:43:24