SwiftUI & MVVM:同一ViewModel多组件加载状态追踪最优方案
SwiftUI多组件ViewModel优化方案
问题1:统一管理多组件的加载状态与数据
你当前每个组件单独维护isXxxLoading和xxxComponent变量的写法会导致代码冗余,推荐用泛型状态封装的方式,把每个组件的加载状态、数据、错误信息合并到一个对象中:
定义泛型状态枚举
enum ComponentLoadState<Data> { case loading case loaded(Data) case failed(Error?) // 可选:按需添加错误状态 }
改造ViewModel
用泛型状态替代零散的状态变量,简化代码结构:
class ViewModel: ObservableObject { @Published private(set) var firstComponentState: ComponentLoadState<FirstComponentModel> = .loading @Published private(set) var secondComponentState: ComponentLoadState<SecondComponentModel> = .loading @Published private(set) var thirdComponentState: ComponentLoadState<ThirdComponentModel> = .loading private let api = SomeApi() func loadFirstComponent() { Task { do { let response = try await api.loadFirstComponent() await MainActor.run { self.firstComponentState = .loaded(response) } } catch { await MainActor.run { self.firstComponentState = .failed(error) } } } } // 第二、第三个组件的加载逻辑复用上述模式即可 }
视图层使用
struct ContentView: View { @StateObject var vm = ViewModel() var body: some View { VStack { switch vm.firstComponentState { case .loading: LoadingView() case .loaded(let data): FirstComponentView(data: data) case .failed(let error): ErrorView(error: error) // 可选:错误状态展示 } // 其他组件同理 } .onAppear { vm.loadFirstComponent() vm.loadSecondComponent() vm.loadThirdComponent() } } }
如果组件数量极多,也可以用[String: ComponentLoadState<Any>]字典管理,但泛型枚举+单独属性的方式类型更安全,优先推荐。
问题2:所有API请求完成后执行自定义代码
有两种实用方案,根据请求触发场景选择:
方案1:用Task Group批量等待所有请求
如果所有请求可以同时触发,直接用withTaskGroup等待全部任务完成:
class ViewModel: ObservableObject { // ... 状态定义 func loadAllComponents() { Task { await withTaskGroup(of: Void.self) { group in group.addTask { await self.loadFirstComponent() } group.addTask { await self.loadSecondComponent() } group.addTask { await self.loadThirdComponent() } // 等待所有任务执行完毕 await group.waitForAll() // 执行自定义逻辑 await MainActor.run { self.handleAllLoaded() } } } } // 改造单个加载方法为异步函数 private func loadFirstComponent() async { do { let response = try await api.loadFirstComponent() await MainActor.run { self.firstComponentState = .loaded(response) } } catch { await MainActor.run { self.firstComponentState = .failed(error) } } } private func handleAllLoaded() { // 这里写所有请求完成后的逻辑,比如隐藏全局加载、上报统计等 print("所有组件加载完成") } }
方案2:监听状态变化判断是否全部完成
如果请求触发时机不统一,通过Combine监听所有组件状态,判断是否全部加载完成:
class ViewModel: ObservableObject { // ... 状态定义 private var cancellables = Set<AnyCancellable>() init() { // 合并三个组件的状态流 Publishers.CombineLatest3($firstComponentState, $secondComponentState, $thirdComponentState) .map { first, second, third in // 判断是否全部进入loaded状态 switch (first, second, third) { case (.loaded, .loaded, .loaded): return true default: return false } } .removeDuplicates() .sink { [weak self] allLoaded in if allLoaded { self?.handleAllLoaded() } } .store(in: &cancellables) } private func handleAllLoaded() { print("所有组件加载完成") } }
这种方案会自动监听状态变化,只要所有组件都加载完成,就会触发后续逻辑。
内容的提问来源于stack exchange,提问作者stompy
相关产品推荐
相关产品推荐

