Post

Replies

Boosts

Views

Activity

Reply to SwiftUI Binding property from view model to a value outside of that view model
I've used 2 different isOn properties just to separate the examples of ToggleWrapperView in the ContentView. In real example it will be only in the DataManager. In actual implementation I have a more complex view with multiple text editors, toggles, and other views to show, so I have multiple smaller reusable views that could be configured with different color, font, etc. DataManager is a type specific for this view and responsible for fetching remote data, applying some local state from Core Data to that remote data and creating multiple structs to be presented in the view. Those structs don't contain presentation data (color, font, etc). ContentViewModel will use those structs from DataManager and create view models for the reusable view components (by adding some presentation data: font, color, background, etc). The reason I've tried to use @Binding in a ToggleViewModel is to connect the data from DataManager to the editable property of the view. Like when user makes a change to the text in text editor or changes the value of the toggle, those changes would propagate to the struct properties inside of DataManager. But since @Binding won't work inside of a view model struct, I would have to use something like this: struct ContentView: View { @StateObject var viewModel = ContentViewModel() var body: some View { VStack { ToggleWrapperView(viewModel: $viewModel.toggleViewModel) } .padding() } } #Preview { ContentView() } struct ToggleWrapperView: View { @Binding var viewModel: ToggleViewModel var body: some View { VStack { Toggle(isOn: $viewModel.isOn, label: { Text(viewModel.title + " \(viewModel.isOn)") }) } } } class ContentViewModel: ObservableObject { @ObservedObject var dataManager = DataManager() @Published var toggleViewModel: ToggleViewModel! { didSet { guard dataManager.dataStruct.isOn != toggleViewModel.isOn else { return } dataManager.dataStruct.isOn = toggleViewModel.isOn } } init() { // In real world this will initialized after DataManager fetches the data toggleViewModel = ToggleViewModel(isOn: dataManager.dataStruct.isOn, title: "Some title") } } struct ToggleViewModel { var isOn: Bool let title: String // This could have some other properties related to configuration of the text and color of the toggle, etc } class DataManager: ObservableObject { // This will be initialized in async method with remote data, etc @Published var dataStruct: SomeDataStruct = .init(isOn: false) struct SomeDataStruct { var isOn: Bool // This could have some other data properties } } With the binding in the view model I was trying to skip the step of manually updating the data in DataManager when ToggleViewModel changes from the action on the view. ToggleViewModel here is only for example, it could be a view model for reusable text editor view, or some other custom views. Also for this example I don't really need the DataManager to be ObservableObject, I might need it in more complex case.
Feb ’24