I might be misunderstanding a couple of key concepts, but not seeing how to properly handle view bindings and retain proper MVVM structure with SwiftUI.
Let's take this example of two fields that affect the text above them:
struct ContentView: View {
@State var firstName = "John"
@State var lastName = "Smith"
var body: some View {
VStack {
Text("first name: \(firstName)")
Text("last name: \(lastName)")
ChangeMeView(firstName: $firstName, lastName: $lastName)
}
}
}
struct ChangeMeView: View {
@Binding var firstName: String
@Binding var lastName: String
var body: some View {
VStack {
TextField("first name", text: $firstName)
TextField("last name", text: $lastName)
}
}
}
Works as expected. However, if I wanted to follow MVVM, wouldn't I need to move (firstName, lastName) to a ViewModel object within that view?
That means that the view starts looking like this:
struct ContentView: View {
@State var firstName = "John"
@State var lastName = "Smith"
var body: some View {
VStack {
Text("first name: \(firstName)")
Text("last name: \(lastName)")
ChangeMeView(firstName: $firstName, lastName: $lastName)
}
}
}
struct ChangeMeView: View {
// @Binding var firstName: String
// @Binding var lastName: String
@StateObject var viewModel: ViewModel
init(firstName: Binding<String>, lastName: Binding<String>) {
//from https://stackoverflow.com/questions/62635914/initialize-stateobject-with-a-parameter-in-swiftui#62636048
_viewModel = StateObject(wrappedValue: ViewModel(firstName: firstName, lastName: lastName))
}
var body: some View {
VStack {
TextField("first name", text: viewModel.firstName)
TextField("last name", text: viewModel.lastName)
}
}
}
class ViewModel: ObservableObject {
var firstName: Binding<String>
var lastName: Binding<String>
init(firstName: Binding<String>, lastName: Binding<String>) {
self.firstName = firstName
self.lastName = lastName
}
}
This works but feels to me like it might be hacky. Is there another smarter way to pass data (like bindings) to a view while retaining MVVM?
Here's an example where I try using @Published. While it runs, the changes don't update the text:
struct ContentView: View {
var firstName = "John"
var lastName = "Smith"
var body: some View {
VStack {
Text("first name: \(firstName)")
Text("last name: \(lastName)")
ChangeMeView(viewModel: ViewModel(firstName: firstName, lastName: lastName))
}
}
}
struct ChangeMeView: View {
@ObservedObject var viewModel: ViewModel
init(viewModel: ViewModel) {
self.viewModel = viewModel
//apple approved strategy from https://stackoverflow.com/questions/62635914/initialize-stateobject-with-a-parameter-in-swiftui#62636048
// _viewModel = StateObject(wrappedValue: ViewModel(firstName: firstName, lastName: lastName))
}
var body: some View {
VStack {
TextField("first name", text: $viewModel.firstName)
TextField("last name", text: $viewModel.lastName)
}
}
}
class ViewModel: ObservableObject {
@Published var firstName: String
@Published var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}