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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 17:17:12