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

SwiftUI中onChange更新EnvironmentObject触发异常回调问题

SwiftUI @Published变量的.onChange回调异常触发问题

我在BranchListViewModel(ObservableObject子类)中使用@Published修饰的inProgress布尔变量跟踪API请求状态:调用setActive()方法时将其设为true,请求结束后设为false。原本在BranchListView中通过.onChange(of: branchViewModel.inProgress)监控该变量,当值为false时调用reloadData,运行正常。

但当我在该onChange回调中更新EnvironmentObject类型的routerViewModel的showLoadingIndicator为当前值时,出现异常:调用branchViewModel.setActive()后,inProgress设为true,onChange先收到true,随即又收到false;待API请求结束将inProgress设为false时,onChange不再触发。而inProgress的didSet显示其值变化正确(true→false)。

异常流程:

branchViewModel.setActive()
inProgress = true
inProgress didSet -> true
.onChange(of:) -> true
.onChange(of:) -> false        <<<<--- 异常点
API call started
API call processing
API call finished
inProgress = false
inProgress didSet -> false

解决方案

  • 将加载状态更新逻辑移到ViewModel内部
    避免在视图的onChange回调中修改EnvironmentObject,防止触发不必要的视图刷新导致状态混乱。直接在BranchListViewModel中同步更新routerViewModel的加载状态:

    class BranchListViewModel: ObservableObject {
        @Published var inProgress = false
        private let routerViewModel: RouterViewModel
    
        init(routerViewModel: RouterViewModel) {
            self.routerViewModel = routerViewModel
        }
    
        func setActive() {
            inProgress = true
            routerViewModel.showLoadingIndicator = true
            // 启动异步API请求
            Task {
                await performAPICall()
                inProgress = false
                routerViewModel.showLoadingIndicator = false
            }
        }
    
        private func performAPICall() async {
            // 你的API请求逻辑
        }
    }
    
  • 用.task替代.onChange处理状态响应
    .task会绑定到指定状态的变化,不会因视图刷新重复触发不必要的回调,更适合处理异步相关的状态响应:

    BranchListView()
        .task(id: branchViewModel.inProgress) {
            routerViewModel.showLoadingIndicator = branchViewModel.inProgress
            if !branchViewModel.inProgress {
                reloadData()
            }
        }
    
  • 排查showLoadingIndicator的其他修改源
    给routerViewModel的showLoadingIndicator添加didSet日志,确认是否有其他视图或逻辑在修改该值,导致触发额外的视图刷新干扰inProgress的回调:

    class RouterViewModel: ObservableObject {
        @Published var showLoadingIndicator = false {
            didSet {
                print("showLoadingIndicator changed: \(oldValue) → \(showLoadingIndicator)")
            }
        }
    }
    

内容的提问来源于stack exchange,提问作者filip.karas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 23:50:16