Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing An ObservedObject To Nested Child Views SwiftUI (SwiftUI Data Flow)

Tags:

I'm trying to understand why passing an @ObservedObject variable does not work for nested child views. The data is able to be passed but the changes only reflects at the root view where the @ObservedObject variable was created. The changes don't show in the subviews. Looking at Apple documentation (which has been updated for Xcode beta 5), the answer seems to be to create both an environment object and a regular variable in order to get the correct index from the environment object. Here is Apples example.

My understanding is that an @ObservedObject can be used to reference a variable from multiple views, but if you want the data to be accessible from any view then you should use an environment object. Therefore I believe that passing the @ObservedObject should be possible. The issue I believe is happening is that since ScreenTwo is passing the @Binding variable to DetailsView and that is what's causing a problem. To solve this I would think you need to keep passing the full @ObservedObject, but then again you would need some type of regular variable to get the proper index.

I feel like all of this should be much more straightforward.

import SwiftUI import Combine  struct Sport: Identifiable{     var id = UUID()     var name : String     var isFavorite = false     var school : String }  final class SportData: ObservableObject  {     @Published var store =         [             Sport(name: "soccer", isFavorite: false, school: "WPI"),             Sport(name: "tennis", isFavorite: false, school: "WPI"),             Sport(name: "swimming", isFavorite: true, school: "WPI"),             Sport(name: "running", isFavorite: true, school: "RIT"),     ] }  struct Testing: View {     @ObservedObject var sports = SportData()      var body: some View {         NavigationView{             List{                 ForEach($sports.store){ sport in                     NavigationLink(destination: ScreenTwo(sport: sport)){                         HStack {                             Text(sport.value.name)                             Spacer()                             Text(sport.value.isFavorite.description)                         }                     }                 }             }         }.navigationBarTitle("Settings")     } }  struct ScreenTwo : View{     @Binding var sport : Sport      var body: some View{         NavigationLink(destination: DetailsView(sport: $sport)){             Text(sport.isFavorite.description)         }     } }  struct DetailsView: View {     @Binding var sport : Sport      var body: some View {         Button(action: {             self.sport.isFavorite.toggle()             self.sport.name = "Ricky"         }) {             Text(sport.isFavorite.description)             Text(sport.name)         }     } }    #if DEBUG struct Testing_Previews: PreviewProvider {     static var previews: some View {         Testing()     } } #endif 
like image 836
Richard Witherspoon Avatar asked Aug 19 '19 06:08

Richard Witherspoon


1 Answers

For ObservableObject the pairing ObservedObject makes view refresh, so to solve the task in question I would recommend the following approach:

Demo

Usage of ObservingObject/ObjservedObject pattern

Code

import SwiftUI import Combine  class Sport: ObservableObject, Hashable, Identifiable {      static func == (lhs: Sport, rhs: Sport) -> Bool {         lhs.name == rhs.name && lhs.isFavorite == rhs.isFavorite && lhs.school == rhs.school     }      func hash(into hasher: inout Hasher) {         hasher.combine(name)         hasher.combine(isFavorite)         hasher.combine(school)     }      @Published var name : String     @Published var isFavorite = false     @Published var school : String      init(name: String, isFavorite: Bool, school: String) {         self.name = name         self.isFavorite = isFavorite         self.school = school     } }  final class SportData: ObservableObject  {     @Published var store =         [             Sport(name: "soccer", isFavorite: false, school: "WPI"),             Sport(name: "tennis", isFavorite: false, school: "WPI"),             Sport(name: "swimming", isFavorite: true, school: "WPI"),             Sport(name: "running", isFavorite: true, school: "RIT"),     ] }  struct TestingObservedObject: View {     @ObservedObject var sports = SportData()      var body: some View {         NavigationView{             List{                 ForEach(sports.store){ sport in                     NavigationLink(destination: ScreenTwo(sport: sport)) {                         HStack {                             Text("\(sport.name)")                             Spacer()                             Text(sport.isFavorite.description)                         }                     }                     .onReceive(sport.$isFavorite) { _ in self.sports.objectWillChange.send() }                 }             }         }.navigationBarTitle("Settings")     } }  struct ScreenTwo : View{     @ObservedObject var sport : Sport      var body: some View{         NavigationLink(destination: DetailsView(sport: sport)){             Text(sport.isFavorite.description)         }     } }  struct DetailsView: View {     @ObservedObject var sport : Sport      var body: some View {         Button(action: {             self.sport.isFavorite.toggle()             self.sport.name = "Ricky"         }) {             Text(sport.isFavorite.description)             Text(sport.name)         }     } }    #if DEBUG struct Testing_Previews: PreviewProvider {     static var previews: some View {         TestingObservedObject()     } } #endif 
like image 186
Asperi Avatar answered Sep 19 '22 21:09

Asperi