首页 > 解决方案 > 将@Binding / @State 绑定到@Publisher 以解耦VM和View Layer

问题描述

我想解耦我的 ViewModel 和 View 层以增加我的 View 的可测试性。因此,我想将我的属性状态保持在视图内,并且只在需要时对其进行初始化。但我无法使用@Published 属性初始化我的@Binding 或@States。有没有办法将它们耦合到 init 函数中?

我只是在下面添加示例代码

代替

import SwiftUI

class ViewModel: ObservableObject {
    @Published var str: String = "a"
    @Published var int: Int = 1 { didSet { print("ViewModel int = \(int)")} }
    
    init() {
        print("ViewModel initialized")
    }
}

struct ContentView: View {
    
    @ObservedObject vM = ViewModel()
    
    var body: some View {
        Button(action: { vM.int += 1; print(int) }, label: {
        Text("Button")
        })
    }
}

我想在我的视图中不使用 @ObservedObject 来实现这一点。

import SwiftUI

class ViewModel: ObservableObject {
    @Published var str: String = "a"
    @Published var int: Int = 1 { didSet { print("ViewModel int = \(int)")} }
    
    init() {
        print("ViewModel initialized")
    }
}

struct ContentView: View {
    
    @Binding var str: String
    @Binding var int: Int
    
    var body: some View {
        Button(action: { int += 1; print(int) }, label: {
        Text("Button")
        })
    }
}

extension ContentView {
    
        init(viewModel:ObservedObject<ViewModel> = ObservedObject(wrappedValue: ViewModel())) {
        // str: Binding<String> and viewModel.str: Published<String>.publisher 
        // type so that I cannot bind my bindings to viewModel. I must accomplish
        // this by using @ObservedObject but this time my view couples with ViewModel
        _str = viewModel.wrappedValue.$str 
        _int = viewModel.wrappedValue.$int
        print("ViewCreated")
    }
    
}



// Testing Init
ContentView(str: Binding<String>, int: Binding<Int>)

// ViewModel Init
ContentView(viewModel: ViewModel)

这样我就不能将它们相互绑定,我只想将我的绑定或状态属性绑定到已发布的属性。

标签: mvvmswiftuiobservableobject

解决方案


我已经意识到通过 Binding(get:{}, set{}),我可以做到这一点。如果有人想分离他们的 ViewModel 和 View 层,他们可以使用这种方法:

import SwiftUI

class ViewModel: ObservableObject {
    @Published var str: String = "a"
    @Published var int: Int = 1 { didSet { print("ViewModel int = \(int)")} }

    init() {
        print("ViewModel initialized")
    }
}

struct ContentView: View {

    @Binding var str: String
    @Binding var int: Int

    var body: some View {
        Button(action: { int += 1; print(int) }, label: {
            Text("Button")
        })
    }
}

extension ContentView {

    init(viewModel:ViewModel = ViewModel()) {
        _str = Binding ( get: { viewModel.str }, set: { viewModel.str = $0 } )
        _int = Binding ( get: { viewModel.int }, set: { viewModel.int = $0 } )

        print("ViewCreated")
    }

}

推荐阅读