I'm having an issue with a Binding property inside of a view model that's being created in another view model. Here is simplified version of 2 views:
struct ContentView: View {
@StateObject var viewModel = ContentViewModel()
var body: some View {
VStack {
// binding through `@Published var toggleViewModel`
ToggleWrapperView(viewModel: viewModel.toggleViewModel)
// binding through `var isOnBinding: Binding<Bool>`
ToggleWrapperView(viewModel: ToggleViewModel(isOn: viewModel.isOnBinding!, title: "Binding through isOnBinding"))
// binding through `@Published var isOn`
ToggleWrapperView(viewModel: ToggleViewModel(isOn: $viewModel.isOn, title: "Binding through view"))
}
.padding()
}
}
struct ToggleWrapperView: View {
var viewModel: ToggleViewModel
var body: some View {
VStack {
Toggle(isOn: viewModel.$isOn, label: {
Text(viewModel.title + " \(viewModel.isOn)")
})
}
}
}
And here are simple view models for the views:
class ContentViewModel: ObservableObject {
@ObservedObject var dataManager = DataManager()
@Published var toggleViewModel: ToggleViewModel!
@Published var isOn: Bool = false // Property for the binding in the ContentView
private var privateIsOnForBinding: Bool = false
var isOnBinding: Binding<Bool>?
init() {
toggleViewModel = ToggleViewModel(isOn: $dataManager.isOn, title: "Binding from DataManager")
initBinding()
}
func initBinding() {
isOnBinding = Binding {
self.privateIsOnForBinding
} set: { updatedValue in
self.privateIsOnForBinding = updatedValue
print(self.isOnBinding!)
}
}
}
struct ToggleViewModel {
@Binding var isOn: Bool
let title: String
}
class DataManager: ObservableObject {
@Published var isOn: Bool = false {
didSet {
print("did set DataManager.isOn \(isOn)")
}
}
}
From the ToggleWrapperView examples above only binding created with $viewModel.isOn is working as expected on toggle action.
In the example with toggleViewModel I can see that the property in DataManager is being updated (print in the property observer), but the binding itself stays false
Binding<Bool>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.(unknown context at $1c5c1c4dc).ObjectLocation<BindingTest.DataManager, Swift.Bool>>, _value: false)
And using var isOnBinding: Binding<Bool> gives me the same result, binding is not being updated
Binding<Bool>(transaction: SwiftUI.Transaction(plist: []), location: SwiftUI.LocationBox<SwiftUI.FunctionalLocation<Swift.Bool>>, _value: false)
This is just a simplified example, but I'd like to understand why 2 versions don't work.
I can make the example with toggleViewModel to work if I mark var viewModel: ToggleViewModel as @Binding in the ToggleWrapperView, and remove the @Binding from the isOn property of ToggleViewModel (first example in the view).
However I'm trying to have the binding for the isOn between ToggleViewModel and DataManager that's being passed in the ContentViewModel if possible.