SwiftUI 依赖注入

Posted

技术标签:

【中文标题】SwiftUI 依赖注入【英文标题】:SwiftUI Dependency Injection 【发布时间】:2021-09-18 20:42:01 【问题描述】:

我有一个 SwiftUI 应用,它是一个基于标签的应用

struct Tab_View: View 
    var body: some View 
        TabView
            Main1_View().tabItem 
                Text("Blah 1")
                Image("TabBar1")
            
        
            Main2_View().tabItem 
                Text("Blah 2")
                Image("TabBar2")
            
        
    

每个视图都有自己的视图控制器

struct Main1_View: View 

    @ObservedObject var viewModel: Main1_ViewModel = Main1_ViewModel()

    var body: some View 
        VStack(spacing:0)
           <<< VIEW CODE >>>
        
    

ViewModel 示例

class Main1_ViewModel: ObservableObject 

    @ObservableObject var settings: GameSettings

    func Randomise()
        dataSource = settings.selectedFramework;
    

GameSettings 类被多个视图模型使用,是一个 ObservableObject,在任何地方都是该类的相同实例。

我的背景是 C#,使用 CastleWindsor 进行依赖注入。

我的问题:是否有相当于传递 GameSettings 实例的 SwiftUI?

【问题讨论】:

【参考方案1】:

由于您要求在另一个 ObservableObject(视图模型)中使用 ObservableObject (GameSettings)并且使用依赖注入,所以事情会有点复杂。

为了将依赖注入的ObservableObject 注入到View,通常的解决方案是使用@EnvironmentObject。但是,您必须将对象从视图传递到其视图模型。在我的示例中,我在onAppear 中完成了该操作。副作用是对象是视图模型上的一个可选属性(您可以通过设置一个虚拟初始值来解决这个问题)。

因为嵌套的ObservableObjects 不适用于@Published 类型(适用于值类型,而不是引用类型),所以您需要确保使用objectWillChangeGameSettings 的任何更改传递给父视图模型,我使用 Combine 完成了。

DispatchQueue.main.asyncAfter 部分只是为了表明当GameSettings 中的值更改时,视图确实实际上会更新。

(请注意,我还更改了您的类型名称以使用 Swift 的骆驼大小写约定)

import SwiftUI
import Combine

struct ContentView: View 
    @StateObject private var settings = GameSettings()
    
    var body: some View 
        TabView 
            Main1View().tabItem 
                Text("Blah 1")
                Image("TabBar1")
            
        
            Main2View().tabItem 
                Text("Blah 2")
                Image("TabBar2")
            
        .environmentObject(settings)
    


struct Main1View: View 

    @EnvironmentObject var settings: GameSettings
    @StateObject var viewModel: Main1ViewModel = Main1ViewModel()

    var body: some View 
        VStack(spacing:0)
            Text("Game settings: \(viewModel.settings?.myValue ?? "no value")")
        .onAppear 
            viewModel.settings = settings
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) 
                settings.myValue = "changed"
            
        
    


class GameSettings : ObservableObject 
    @Published var myValue : String = "Test"


class Main1ViewModel: ObservableObject 
    private var cancellable : AnyCancellable?
    var settings: GameSettings? 
        didSet 
            print("Running init on Main1ViewModel")
            self.objectWillChange.send()
            cancellable = settings?.objectWillChange.sink(receiveValue:  _ in
                print("Sending...")
                self.objectWillChange.send()
            )
        
    


struct Main2View : View 
    var body: some View 
        Text("Hello, world!")
    

【讨论】:

这回答了你的问题吗?

以上是关于SwiftUI 依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

如何调整我的视图模型以实现依赖注入 swiftui(用于以后的单元测试)

IOC 控制反转Android 视图依赖注入 ( 视图依赖注入步骤 | 视图依赖注入代码示例 )

IOC 控制反转Android 布局依赖注入 ( 布局依赖注入步骤 | 布局依赖注入代码示例 )

依赖注入 ---- 系列文章

依赖注入

PHP如何实现依赖注入