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

SwiftUI视图在API获取新数据时未更新的问题求助

问题分析与解决方案

你的视图仅显示首次获取的数据、无法随API新数据刷新,核心原因有两点:

1. 仅执行了一次数据请求

当前代码只在视图onAppear时调用fetch()一次,之后不会主动重新拉取API数据,自然无法获取到最新的公交实时信息。

2. Hashable实现的潜在冲突

虽然BusAPI遵循了Hashable,但默认自动合成的Hash实现,会在API返回数据的属性(比如btime2)更新时,导致ForEach无法正确识别列表项的变化——因为id: \.self依赖整个对象的哈希值,属性变化会改变哈希值,ForEach会误判为全新元素,引发刷新异常。


修复方案

第一步:添加定时刷新机制

在ViewModel中加入定时器,定期调用fetch()拉取最新数据:

class ViewModel: ObservableObject {
    @Published var bus: [BusAPI] = []
    private var refreshTimer: Timer?
    
    init() {
        // 初始化时立即请求,之后每30秒刷新一次(可按需调整间隔)
        fetch()
        startRefreshTimer()
    }
    
    func fetch() {
        guard let url = URL(string: "http://telematics.oasa.gr/api/?act=getStopArrivals&p1=060475") else {
            return
        }
        let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
            guard let data = data, error == nil else {
                return
            }
            do {
                let bus = try JSONDecoder().decode([BusAPI].self, from: data)
                DispatchQueue.main.async {
                    self?.bus = bus
                }
            } catch {
                print(error)
            }
        }
        task.resume()
    }
    
    private func startRefreshTimer() {
        refreshTimer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weak self] _ in
            self?.fetch()
        }
    }
    
    // 销毁时停止定时器,避免内存泄漏
    deinit {
        refreshTimer?.invalidate()
    }
}

第二步:优化Hashable实现,使用唯一标识

修改BusAPI,基于唯一标识(比如veh_code,每辆车的编号唯一)手动实现Hashable,让ForEach能精准识别列表项更新:

struct BusAPI: Hashable, Codable {
    let route_code: String
    let veh_code: String
    let btime2: String
    
    // 仅基于唯一的veh_code生成哈希值
    func hash(into hasher: inout Hasher) {
        hasher.combine(veh_code)
    }
    
    // 仅通过veh_code判断对象是否相等
    static func == (lhs: BusAPI, rhs: BusAPI) -> Bool {
        return lhs.veh_code == rhs.veh_code
    }
}

同时修改视图中的ForEach,明确指定唯一id(可选,但更清晰):

ForEach(viewModel.bus, id: \.veh_code) { bus in
    // 原有视图内容
}

第三步:移除重复的请求调用

由于ViewModel初始化时已执行fetch(),可以删除视图中onAppear里的重复调用:

// 移除这段代码
// .onAppear {
//     viewModel.fetch()
// }

额外说明

  • 定时器间隔可根据API更新频率调整,避免过于频繁请求触发限流。
  • 如果veh_code不是唯一标识,可以组合route_code与veh_code作为判断依据。

内容的提问来源于stack exchange,提问作者devgreg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 01:17:37