基于WWDC示例,其他ViewModel如何获取NavigationModel引用?
一、构造函数直接注入NavigationModel
这是最直接且符合依赖注入原则的方案,通过初始化方法将NavigationModel实例传递给ChildViewModel,让其内部可以直接访问并更新属性。
代码示例:
// 修改ChildViewModel,添加带参数的初始化方法 struct ChildViewModel { private let navigationModel: NavigationModel init(navigationModel: NavigationModel) { self.navigationModel = navigationModel } func childViewCalledAndDidSomething() { // 直接更新recipePath,比如添加新的Recipe实例 let newRecipe = Recipe(/* 初始化参数 */) navigationModel.recipePath.append(newRecipe) } }
在使用ChildViewModel的视图中,从环境或父视图获取NavigationModel并传入:
struct ChildView: View { @EnvironmentObject private var navigationModel: NavigationModel var body: some View { // 初始化ViewModel时传入NavigationModel let viewModel = ChildViewModel(navigationModel: navigationModel) Button("触发操作") { viewModel.childViewCalledAndDidSomething() } } }
二、通过Combine实现事件解耦(适合低耦合场景)
如果希望ChildViewModel和NavigationModel不直接耦合,可以使用Combine的发布订阅机制,让NavigationModel监听更新事件并自行处理。
代码示例:
首先扩展NavigationModel,添加用于接收更新的Subject和处理逻辑:
import Combine final class NavigationModel: ObservableObject, Codable { @Published var selectedCategory: Category? @Published var recipePath: [Recipe] @Published var columnVisibility: NavigationSplitViewVisibility // 定义用于接收路径更新的Subject private let recipePathUpdateSubject = PassthroughSubject<[Recipe], Never>() private var cancellables = Set<AnyCancellable>() init() { // 订阅更新事件,自动同步到recipePath recipePathUpdateSubject .assign(to: &$recipePath) } // 对外暴露发送更新的方法 func sendRecipePathUpdate(_ newPath: [Recipe]) { recipePathUpdateSubject.send(newPath) } }
然后修改ChildViewModel,持有NavigationModel的更新方法引用:
struct ChildViewModel { private let updateRecipePath: ([Recipe]) -> Void init(updateRecipePath: @escaping ([Recipe]) -> Void) { self.updateRecipePath = updateRecipePath } func childViewCalledAndDidSomething() { let newPath = /* 生成新的导航路径数组 */ updateRecipePath(newPath) } }
在视图中初始化时传入更新方法:
struct ChildView: View { @EnvironmentObject private var navigationModel: NavigationModel var body: some View { let viewModel = ChildViewModel(updateRecipePath: navigationModel.sendRecipePathUpdate(_:)) Button("触发操作") { viewModel.childViewCalledAndDidSomething() } } }
三、通过视图回调间接更新(适合ViewModel不直接依赖导航模型的场景)
让ChildViewModel通过回调将需要更新的路径传递给视图,再由视图从环境中获取NavigationModel完成更新。
代码示例:
struct ChildViewModel { func childViewCalledAndDidSomething(completion: ([Recipe]) -> Void) { let newPath = /* 处理后得到的新导航路径 */ completion(newPath) } } struct ChildView: View { @EnvironmentObject private var navigationModel: NavigationModel @StateObject private var viewModel = ChildViewModel() var body: some View { Button("触发操作") { viewModel.childViewCalledAndDidSomething { newPath in navigationModel.recipePath = newPath } } } }
推荐方案
优先选择构造函数注入,它逻辑清晰、易于测试,完全符合WWDC展示的SwiftUI架构中依赖管理的最佳实践,能让代码的依赖关系一目了然。
内容的提问来源于stack exchange,提问作者erotsppa
相关产品推荐
相关产品推荐

