0

I am facing an issue where the view does not update automatically with the @Observable object.

struct Person: Identifiable {
  var id: Int
  var name: String

  init(id: Int, name: String){
      self.id = id
      self.name = name
  }

}

class People: ObservableObject {
  @Published var people: [Person]
  @Published var kitchens: [Kitchen]

  init(){
      self.people = [
          Person(id: 1, name:"Javier"),
          Person(id: 2, name:"Juan"),
          Person(id: 3, name:"Pedro")]
 }
}

struct ContentView: View {
@ObservedObject var mypeople: People

var body: some View {
    VStack{
        ForEach(mypeople.people){ person in
            Text("\(person.name)")
        }
        Button(action: {
            self.mypeople.people.append(Person(id: 7, name: "New person"))
        }) {
            Text("Add/Change name")
        }
    }.onAppear {
        self.mypeople.people.append(Person(id: 7, name: "New person"))
    }
}
}

So, what I want to do is that I have to add a new person, in onAppear instead of adding a new person on tap of the button. In short, the people array lets say, already has three persons and I have to add the fourth one just while landing on that view.

The weird thing is that person gets added if this view if the root view, however, it does not work when this view is pushed using Navigation Link. In the second case, a new person is added and removed right away keeping the people array count to three.

However, the new person easily gets added on the tap of the button. I am stuck here for several hours. Any help would be more than appreciated!! Thanks in Advance!

EDIT

I debugged little more to find out the issue. So, the new person is added in the below scenarios automatically in onAppear:

1) If the ContentView is the root view of the application. 2) If the ContentView is pushed from the very first view in the hierarchy of the parent view. Means, if there are subViews of the ContentView. The Navigation Link from the subViews creates this problem, however, the navigation link directly from the ContentView adds the person successfully.

And for creating a navigationLink, I used:

NavigationLink(destination:  ContentView()) {
     Text("View All")
}
Amrit Sidhu
  • 1,870
  • 1
  • 18
  • 32
  • Would you show code how you manage it in case of NavigationLink? – Asperi Apr 15 '20 at 19:23
  • Actually the issue is in Navigation link itself. I am adding some more description. – Amrit Sidhu Apr 15 '20 at 19:46
  • @Asperi : I have edited the post – Amrit Sidhu Apr 15 '20 at 19:55
  • I'm not able to reproduce your problem. Please update your code to include everything needed to see the problem in action. For example, you never show us where the People() object gets created. It seems the problem you are having is that the People object gets "overwritten", but since we don't see that part of your code, I cannot tell for sure. – kontiki Apr 16 '20 at 08:22

2 Answers2

1

does this addresses the scenario you mention in your comment:

struct TheNavView: View {
@ObservedObject var mypeople: People
@State var newId = Int(arc4random())
var body: some View {
    // the view that create "New person" onAppear
    VStack{
        ForEach(mypeople.people){ person in
            Text("\(person.name)")
        }
        Button(action: {
            self.mypeople.people.append(Person(id: self.newId, name: "New person"))
        }) {
            Text("Add/Change name")
        }
    }.onAppear {
        print("--------> onAppear self.newId: \(self.newId)")
        self.mypeople.people.append(Person(id: self.newId, name: "New person"))
    }
}
}

struct TheSubView: View {
@ObservedObject var mypeople: People
var body: some View {
    // the subView that pushes using NavigationLink
    NavigationView {
        NavigationLink(destination: TheNavView(mypeople: mypeople)) {
            Text("View All")
        }
    }.navigationViewStyle(StackNavigationViewStyle())
}
}

struct ContentView: View {
@ObservedObject var mypeople: People
var body: some View {
    // the ContentView with a subView
    TheSubView(mypeople: mypeople)
}
}
  • Yes, this works perfectly fine. However, in my case there is some sort of problem, that doesnot let the view update. – Amrit Sidhu Apr 22 '20 at 15:26
  • In my case I cannot pass Observed class to the destination view. My destination view has its own observed class. In this scenario, if the destination view is the root view, it works perfectly fine, however if the destination view is pushed from a subView, then it doesnot update. – Amrit Sidhu Apr 22 '20 at 15:28
0

ok, if I understand it correctly, you want to add a new person, in onAppear instead of adding a new person on tap of the button. But the person gets added if this view is the root view, however, it does not work when this view is pushed using Navigation Link.

So I cooked up a test that works:

struct Person: Identifiable {
var id: Int
var name: String
init(id: Int, name: String){
    self.id = id
    self.name = name
}
}

struct Kitchen: Identifiable {
var id: Int
var name: String
init(id: Int, name: String){
    self.id = id
    self.name = name
}
}

class People: ObservableObject {
@Published var people: [Person]
@Published var kitchens: [Kitchen]
init() {
    self.people = [ Person(id: 1, name:"Javier"),Person(id: 2, name:"Juan"),Person(id: 3, name:"Pedro")]
    self.kitchens = [ Kitchen(id: 1, name:"Kitchen1"),Kitchen(id: 2, name:"Kitchen2"), Kitchen(id: 3, name:"Kitchen3")]
}
}

struct SecondView: View {
@ObservedObject var mypeople: People
@State var newId = Int(arc4random())
var body: some View {
    VStack{
        ForEach(mypeople.people){ person in
            Text("\(person.name)")
        }
        Button(action: {
            self.mypeople.people.append(Person(id: self.newId, name: "New person"))
        }) {
            Text("Add/Change name")
        }
    }.onAppear {
        print("--------> onAppear")
        self.mypeople.people.append(Person(id: self.newId, name: "New person"))
    }
}
}

struct ContentView: View {
@ObservedObject var mypeople: People
var body: some View {
    NavigationView {
        NavigationLink(destination: SecondView(mypeople: mypeople)) {
            Text("View All")
        }
    }.navigationViewStyle(StackNavigationViewStyle())
}
}

of course in SceneDelegate, you have to declare:

var people = People()
window.rootViewController = UIHostingController(rootView: ContentView(mypeople: people))
  • The approach you mention ed will work without any issues. Issue comes in when the ContentView has some subView inside, that pushes through the navigation link, from the subView itself and not from the Content View. – Amrit Sidhu Apr 16 '20 at 18:08