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

如何基于SwiftUI中ObservableObject的@Published数组可靠更新String?

问题根因

你的代码存在两个核心问题导致更新异常:

  1. 两个.onReceive修饰符挂载在if let分支内的Text视图上,当recentEvents数组发生变化时,该Text视图可能被销毁重建,导致状态更新时序异常,且闭包捕获的上下文可能不是最新值。
  2. 单独维护timeAgo作为@State变量,额外增加了状态同步的复杂度,很容易出现和DataStore中数据不一致的情况。

修复方案

直接移除冗余的timeAgo状态,将时间字符串改为实时计算的属性,仅用定时器触发视图刷新即可,修改后的视图代码如下:

struct EventsView: View {
    @EnvironmentObject var store: DataStore
    private let timer = Timer.publish(every: 60, on: .main, in: .common).autoconnect()
    @State private var refreshTrigger = 0

    private var timeAgoText: String {
        guard let latestEvent = store.recentEvents.first else {
            return "No Event Data"
        }
        return relativeTimeFormatter.localizedString(for: latestEvent.timestamp, relativeTo: Date())
    }

    var body: some View {
        VStack {
            Text(timeAgoText)
                .fontWeight(.bold)
                .font(.system(.largeTitle, design: .rounded))
        }
        .frame(minWidth: 250, maxWidth: 250, minHeight: 200, maxHeight: 200)
        .onReceive(timer) { _ in
            refreshTrigger += 1
        }
    }
}

private let relativeTimeFormatter: RelativeDateTimeFormatter = {
    let formatter = RelativeDateTimeFormatter()
    formatter.dateTimeStyle = .numeric
    return formatter
}()

因为DataStore是EnvironmentObject,它的@Published属性变更时会自动触发视图重绘,不需要额外监听store.$recentEvents,从根源上避免了状态不同步的问题。

其他优化建议

  • DataStore层优化:CKQueryOperation的completionBlock默认在后台队列执行,建议在调用promise之前就切换到主队列,避免潜在的线程问题;当前的fetchEvents方法没有错误处理,即使CloudKit请求失败也会返回空数组,建议新增失败状态暴露给视图层,方便做异常提示。
  • 可以在DataStore中新增一个@Published var latestEvent: Event?属性,拉取到数据后直接更新该属性,不需要视图层每次取数组第一个元素,逻辑更清晰。
  • 可以监听App的前后台状态,在App进入后台时暂停定时器,回到前台时重新启动,减少不必要的资源消耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 04:15:03