Reputation: 2698
How to pass a @Binding
into a ViewModel.
Let's consider a simple example,
struct TopView: View {
@State var isPresented: Bool
var body: some View {
SubView(isPresented: $isPresented)
}
}
struct SubView: View {
@Binding var isPresented: Bool
}
Then, if we want to a MVVM pattern,
Is it safe to use @Binding
in an ObservableObject
?
For example, is it safe to write,
struct TopView: View {
@State var isPresented: Bool
var body: some View {
SubView(model: SubViewModel(isPresented: $isPresented))
}
}
struct SubView: View {
@ObservedObject var model: SubViewModel
}
// In "SubViewModel.swift"
import Foundation
import Combine
import SwiftUI
public final class SubViewModel: ObservedObject {
@Binding var isPresented: Bool
public init(isPresented: Binding<Bool>) {
self._isPresented = isPresented
}
}
How would you do it? Similar including how to pass environmentObject into a view model?
Upvotes: 2
Views: 848
Reputation: 29242
You are describing the basic use of an ObservableObject
.
@Binding
and @State
are only used in a View
The Apple SwiftUI Tutorials might be very helpful so you understand the SwiftUI concepts. https://developer.apple.com/tutorials/swiftui/
There are some basic mistakes in your code I mentioned the changes in the code below.
import SwiftUI
struct PassingBinding: View {
var body: some View {
TopView1()//Best Practice
//TopView2()//Singleton Pattern
}
}
///This is the best way to do it
///https://developer.apple.com/tutorials/swiftui/handling-user-input
struct TopView1: View {
@StateObject var model: SubViewModel = SubViewModel()
var body: some View {
VStack{
SubView1().environmentObject(model)
Button(action: {
self.model.isPresented.toggle()
}, label: {
Text("Toggle isPresented")
})
}
}
}
struct SubView1: View {
@EnvironmentObject var model: SubViewModel
var body: some View {
Text("SubView - isPresented == \(model.isPresented.description)")
}
}
///Another way that has some specifc uses is to use a Singleton model
///https://developer.apple.com/documentation/swift/cocoa_design_patterns/managing_a_shared_resource_using_a_singleton
struct TopView2: View {
@StateObject var model: SubViewModel = SubViewModel.shared
var body: some View {
VStack{
SubView2()
Button(action: {
self.model.isPresented.toggle()
}, label: {
Text("Toggle isPresented")
})
} }
}
struct SubView2: View {
@StateObject var model: SubViewModel = SubViewModel.shared
var body: some View {
Text("SubView - isPresented == \(model.isPresented.description)")
}
}
///This item can be Observed "ObservableObject" vs I am Observing this object "ObservedObject"
///https://developer.apple.com/documentation/combine/observableobject
public final class SubViewModel: ObservableObject {
static let shared: SubViewModel = SubViewModel()//Singleton Pattern
@Published var isPresented: Bool = false //Initialize here this is the source for the View
//@Binding and @State is are only used in View struct not in an ObservableObject.https://developer.apple.com/documentation/swiftui/binding
}
struct PassingBinding_Previews: PreviewProvider {
static var previews: some View {
PassingBinding()
}
}
Upvotes: 2