MVVM架构下如何从ViewModel更新NavigationStack的path属性?
给你几个符合MVVM规范的解决方案,帮你解决ViewModel更新NavigationStack路径的问题:
方案1:给ViewModel注入Navigation实例
直接把Navigation对象传入ViewModel,让ViewModel持有它的引用,这样API调用成功后就能直接操作path属性。这种方式遵循依赖注入原则,ViewModel负责业务逻辑(API调用),同时可以调用导航服务完成页面跳转,没有破坏MVVM的职责划分。
修改后的ViewModel代码:
@MainActor final class ViewModel: ObservableObject { private let navigation: Navigation // 初始化时注入Navigation实例 init(navigation: Navigation) { self.navigation = navigation } func myApiFunction() { // 模拟API调用 Task { let success = await callApi() if success { // 直接更新导航路径,示例为添加Settings页面 navigation.path.append(.Settings) // 若需要替换整个路径可写:navigation.path = [.Home] } } } private func callApi() async -> Bool { // 实际API逻辑,这里模拟延迟返回成功 try? await Task.sleep(nanoseconds: 1_000_000_000) return true } }
View中初始化ViewModel时传入navigation:
//... @EnvironmentObject var navigation: Navigation @StateObject private var viewModel: ViewModel init() { _viewModel = StateObject(wrappedValue: ViewModel(navigation: navigation)) } //...
方案2:用闭包回调(并非不符合MVVM)
很多人误以为闭包回调违背MVVM,但其实ViewModel的核心职责是处理业务逻辑,导航属于UI层行为。ViewModel只需要在业务完成后通知View,由View执行导航操作,这反而更符合“ViewModel不直接操作UI”的原则。
修改后的ViewModel代码:
@MainActor final class ViewModel: ObservableObject { func myApiFunction(onSuccess: @escaping () -> Void) { Task { let success = await callApi() if success { onSuccess() } } } private func callApi() async -> Bool { try? await Task.sleep(nanoseconds: 1_000_000_000) return true } }
View中调用时传入导航逻辑:
Button("Make API-Call") { viewModel.myApiFunction { navigation.path.append(.Settings) } }
方案3:用Combine发布导航事件(解耦性更强)
如果想让ViewModel完全不依赖Navigation类,可以定义一个导航事件的Publisher,View订阅这个事件后更新路径。这种方式解耦度最高,ViewModel只负责发布业务完成后的导航需求,具体导航实现由View处理。
修改后的ViewModel代码:
import Combine @MainActor final class ViewModel: ObservableObject { // 定义导航事件的Publisher let navigationEvent = PassthroughSubject<pathCases, Never>() func myApiFunction() { Task { let success = await callApi() if success { // 发布导航事件,告知需要跳转到Settings页面 navigationEvent.send(.Settings) } } } private func callApi() async -> Bool { try? await Task.sleep(nanoseconds: 1_000_000_000) return true } }
View中订阅事件:
//... @EnvironmentObject var navigation: Navigation @StateObject private var viewModel = ViewModel() private var cancellables = Set<AnyCancellable>() init() { viewModel.navigationEvent .receive(on: DispatchQueue.main) .sink { [weak self] destination in self?.navigation.path.append(destination) } .store(in: &cancellables) } //...
内容的提问来源于stack exchange,提问作者J. Mann
相关产品推荐
相关产品推荐

