SwiftUI中Observable对象内结构体属性无法触发视图刷新的问询
SwiftUI @Observable 与值类型属性的视图更新问题
我在探索SwiftUI的@Observable特性,尝试通过Environment实现依赖注入时,搭建了以下代码结构:
初始代码实现
状态与数据仓库定义
@Observable class AppState { var dataRepository = DataRepository() } struct DataRepository { private(set) var isLoading = false mutating func downloadData() async -> String { isLoading = true try? await Task.sleep(for: .seconds(3)) isLoading = false return "Some data" } }
应用与视图注入
@main struct DependencyInjectionApp: App { var body: some Scene { WindowGroup { ContentView() } .environment(AppState()) } } struct ContentView: View { @Environment(AppState.self) private var appState @State private var name: String = "" var body: some View { Group { if appState.dataRepository.isLoading { ProgressView() } else { Text(name) } } .padding() .task { name = await appState.dataRepository.downloadData() } } } #Preview { ContentView() .environment(AppState()) }
运行现象
- 变量
name最终成功更新为"Some data" - 加载过程中未显示ProgressView
问题根源
- 下载完成后,
name的@State属性更新触发了视图重绘 isLoading是Observable类AppState内部值类型DataRepository的属性,无法触发视图重绘
疑问解答
这属于@Observable的特性限制,你的理解存在偏差:
@Observable只会追踪类本身的属性赋值操作,当属性是值类型时,只有对该属性的整体赋值(比如appState.dataRepository = DataRepository())才会被感知到。- 值类型内部的属性变更(比如
dataRepository.isLoading = true)属于值类型的内部修改,不会触发外部Observable类的属性赋值事件,因此SwiftUI无法捕获到这个变化,也就不会触发视图重绘。
可行调整方案
方案1:将
isLoading移至AppState
把状态直接放在Observable类中,利用@Observable的特性触发重绘:@Observable class AppState { var isLoading = false var dataRepository = DataRepository() func downloadData() async -> String { isLoading = true let result = await dataRepository.downloadData() isLoading = false return result } } struct DataRepository { func downloadData() async -> String { try? await Task.sleep(for: .seconds(3)) return "Some data" } }方案2:将
DataRepository改为@Observable类
让数据仓库本身成为Observable类,其内部属性变更会被SwiftUI追踪:@Observable class AppState { var dataRepository = DataRepository() } @Observable class DataRepository { private(set) var isLoading = false func downloadData() async -> String { isLoading = true try? await Task.sleep(for: .seconds(3)) isLoading = false return "Some data" } }
内容的提问来源于stack exchange,提问作者Adam Carter
相关产品推荐
相关产品推荐

