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

SwiftUI中View未响应链式ObservedObject的Published属性变更如何解决

问题根因

@ObservedObject 是 SwiftUI 专用的属性修饰符,仅能在 SwiftUI 视图结构体中生效,你在作为 ObservableObject 的 ViewModel 中使用它不会产生任何监听效果。
ObservableObject 默认只会对自身被 @Published 修饰的属性变更发送通知,嵌套的 ObservableObject(你代码里的 AuthenticationService)的属性变更不会自动向上冒泡触发上层 ViewModel 的通知,因此视图收不到刷新信号。


可选解决方案

方案1:直接在视图层监听服务(改动最小)

直接把 AuthenticationService 放到视图层作为 @ObservedObject,跳过 ViewModel 的中转,服务的 user 属性变更会直接触发视图刷新:

struct WelcomeView: View {
    // 直接监听服务
    @ObservedObject private var authenticationService: AuthenticationService
    @State private var signInActive: Bool = false
    
    init(authenticationService: AuthenticationService) {
        self.authenticationService = authenticationService
    }
    
    var body: some View {
        if authenticationService.user != nil {
            HomeView()
        } else { 
            LoginView()
        }
    } 
}

方案2:ViewModel 手动转发变更(保留现有层级)

如果需要保留 View->ViewModel->Service 的架构分层,可在 ViewModel 中通过 Combine 监听服务的变更,手动触发自身的通知:

import Combine

extension WelcomeView {
    final class Model: ObservableObject {
        var authenticationService: AuthenticationService
        // 存储订阅关系
        private var cancellable = Set<AnyCancellable>()
        
        init(authenticationService: AuthenticationService) {
            self.authenticationService = authenticationService
            // 监听服务的变更,转发给当前 ViewModel
            authenticationService.objectWillChange
                .receive(on: DispatchQueue.main)
                .sink { [weak self] _ in
                    self?.objectWillChange.send()
                }
                .store(in: &cancellable)
        }
    }
}

方案3:ViewModel 封装用户状态(更符合 MVVM 封装原则)

不把服务直接暴露给视图,在 ViewModel 中新增 @Published 修饰的用户状态属性,中转服务的用户变更,视图只需要和 ViewModel 的属性交互:

import Combine

extension WelcomeView {
    final class Model: ObservableObject {
        @Published var currentUser: User?
        private var authenticationService: AuthenticationService
        private var cancellable = Set<AnyCancellable>()
        
        init(authenticationService: AuthenticationService) {
            self.authenticationService = authenticationService
            // 中转服务的user属性变更
            authenticationService.$user
                .receive(on: DispatchQueue.main)
                .assign(to: &$currentUser)
        }
    }
}

对应的视图代码调整为读取 ViewModel 的 currentUser 属性:

var body: some View {
    if viewModel.currentUser != nil {
        HomeView()
    } else { 
        LoginView()
    }
}

额外优化建议

如果你最低支持 iOS 14 及以上版本,视图中对 ViewModel 的引用建议替换为 @StateObject 而非 @ObservedObject,避免视图意外刷新时 ViewModel 被重复创建导致的异常问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 11:45:04