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

基于WWDC示例,其他ViewModel如何获取NavigationModel引用?

解决ChildViewModel更新NavigationModel中recipePath的方案

一、构造函数直接注入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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 08:12:56