I'm trying to create a simple NavigationView showing a list of users and be able to edit them in a child view, while using ObservableObject. The only way I could the ObservedObjects to automatically update between the 2 views is to pass the entire users collection from the parent to the child View so that I could call self.users.objectWillChange.send().
This seems inefficient. Is there a better way to do this?
This seems inefficient. Is there a better way to do this?
Model Classes
Code Block swift class User: Identifiable, ObservableObject { let id = UUID() @Published var name: String init(name: String) { self.name = name } }
Code Block swift class Users: ObservableObject { @Published var users: [User] init() { self.users = [ User(name: "John Doe"), User(name: "Jane Doe"), ] } }
Views
Code Block swift struct TestNavigationView: View { @ObservedObject var users = Users() var body: some View { NavigationView { List(self.users.users) { user in NavigationLink(destination: UserDetail(user: user, users: self.users) ) { Text(user.name) } } }.navigationBarTitle("Users") } }
Code Block swift struct UserDetail: View { @ObservedObject var user: User @ObservedObject var users: Users var body: some View { VStack { TextField("Enter Name", text: $user.name) Button(action: { self.users.objectWillChange.send() }, label: { Text("Save") }) } } }
If you want to utilize the feature creating Binding from @ObservedObject, you may need to write your TestNavigationView like this:
This code passes Binding<User> (whole User, not only Binding<String> representing User.name) to UserDetail. Isn't it good for future extension, like User hold other properties than name edited in detail view.
With the above change, your UserDetail would become like this:
Code Block struct TestNavigationView: View { @ObservedObject var users = Users() var body: some View { NavigationView { List(self.users.users.indices) { index in NavigationLink(destination: UserDetail(user: self.$users.users[index]) ) { Text(self.users.users[index].name) } } }.navigationBarTitle("Users") } }
This code passes Binding<User> (whole User, not only Binding<String> representing User.name) to UserDetail. Isn't it good for future extension, like User hold other properties than name edited in detail view.
With the above change, your UserDetail would become like this:
Code Block struct UserDetail: View { @Environment(\.presentationMode) var presentationMode @Binding var user: User var body: some View { VStack { TextField("Enter Name", text: $user.name) Button(action: { self.presentationMode.wrappedValue.dismiss() }, label: { Text("Save") }) } } }