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

如何让SwiftUI响应ViewModel的状态变化进行刷新?

在SwiftUI中使用基于Combine CurrentValueSubject的ViewModel

步骤1:让ViewModel适配SwiftUI的状态监听

你的ViewModel需要遵循SwiftUI的ObservableObject协议,这样SwiftUI才能自动感知状态变化。结合已有的CurrentValueSubject,有两种适配方式:

方式A:将CurrentValueSubject同步到@Published属性

保留原有Combine逻辑的同时,通过@Published属性给SwiftUI提供可监听的状态:

import Combine
import SwiftUI

// 定义状态枚举
enum ViewModelState {
    case idle
    case loading
    case success(data: [String])
    case error(message: String)
}

class MyViewModel: ObservableObject {
    // 原有状态管理的CurrentValueSubject
    private let stateSubject = CurrentValueSubject<ViewModelState, Never>(.idle)
    // SwiftUI可监听的@Published状态
    @Published private(set) var state: ViewModelState = .idle
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        // 订阅stateSubject,同步更新@Published属性
        stateSubject
            .assign(to: \.state, on: self)
            .store(in: &cancellables)
        
        // 触发初始网络请求
        fetchData()
    }
    
    func fetchData() {
        // 模拟网络服务返回的Combine发布者
        let networkPublisher = Future<[String], Error> { promise in
            DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
                promise(.success(["条目1", "条目2", "条目3"]))
            }
        }
        
        stateSubject.send(.loading)
        
        networkPublisher
            .receive(on: DispatchQueue.main)
            .sink(receiveCompletion: { [weak self] completion in
                switch completion {
                case .failure(let error):
                    self?.stateSubject.send(.error(message: error.localizedDescription))
                case .finished: break
                }
            }, receiveValue: { [weak self] data in
                self?.stateSubject.send(.success(data: data))
            })
            .store(in: &cancellables)
    }
}

方式B:直接利用CurrentValueSubject触发状态通知

如果不想额外维护@Published属性,可以手动通过objectWillChange发布状态更新信号:

class MyViewModel: ObservableObject {
    let stateSubject = CurrentValueSubject<ViewModelState, Never>(.idle)
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        // 当stateSubject有新值时,通知SwiftUI视图更新
        stateSubject
            .sink { [weak self] _ in
                self?.objectWillChange.send()
            }
            .store(in: &cancellables)
        
        fetchData()
    }
    
    // fetchData方法同方式A
}

步骤2:在SwiftUI视图中绑定ViewModel

在SwiftUI视图中通过@StateObject(iOS 14+)或@ObservedObject(iOS 13)持有ViewModel,根据状态渲染UI:

struct MySwiftUIView: View {
    // iOS 14+推荐用@StateObject,确保ViewModel生命周期与视图绑定
    @StateObject private var viewModel = MyViewModel()
    // iOS 13需用@ObservedObject,需从父视图传入ViewModel实例
    // @ObservedObject var viewModel: MyViewModel
    
    var body: some View {
        VStack(spacing: 20) {
            switch viewModel.state {
            case .idle:
                Text("点击按钮加载数据")
            case .loading:
                ProgressView("加载中...")
            case .success(let data):
                List(data, id: \.self) { item in
                    Text(item)
                }
            case .error(let message):
                VStack {
                    Text("加载失败")
                    Text(message)
                        .foregroundColor(.red)
                }
            }
            
            Button("重新加载") {
                viewModel.fetchData()
            }
            .disabled(viewModel.state == .loading)
        }
        .padding()
    }
}

关键注意事项

  • @StateObject vs @ObservedObject:iOS 14及以上优先用@StateObject,它能自动管理ViewModel的生命周期;iOS 13则用@ObservedObject,但需要确保父视图正确传入实例。
  • 线程安全:网络请求的结果必须切换到主线程(通过receive(on: DispatchQueue.main)),避免后台线程更新UI状态。
  • 内存管理:订阅时使用[weak self]避免循环引用,用Set<AnyCancellable>管理所有订阅的生命周期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 04:42:11