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
相关产品推荐
相关产品推荐

