I want to load different data in the same View using an ObservableObject with an Index.
As you can see in the simple demo example attached when I go through the views changing the id in order to load different data it works.
But when in any point I change another data from another observable object (in this case AnotherObservable) then my view breaks and any data is found at all.
Navigation works...
Once I press Change Title button...
To test de issue just simple copy and paste the code and navigate through the data with the <- -> and then press Change title button
class DemoItemsLoader: ObservableObject {
@Published var items = [1, 2, 3, 4]
}
class DemoArticleLoader: ObservableObject {
@Published var items = [1 : "One", 2 : "Two" , 3 : "Three", 4 : "Four"]
@Published var realData: String?
func loadItemForID(id: Int) {
realData = items[id] ?? "Not found"
}
}
class AnotherObservable: ObservableObject {
@Published var title: String = "Title"
}
struct Class1: View {
@StateObject var anotherObservable = AnotherObservable()
@StateObject var itemsLoader = DemoItemsLoader()
var body: some View {
NavigationView{
VStack {
ForEach(itemsLoader.items, id: \.self) { item in
NavigationLink(
destination: Class2(anotherObservable: anotherObservable, articleLoader: DemoArticleLoader(), id: item)) {
Text("\(item)")
}
}
}
}
}
}
struct Class2: View {
@ObservedObject var anotherObservable: AnotherObservable
@ObservedObject var articleLoader: DemoArticleLoader
@State var id: Int
var body: some View {
VStack {
Text(anotherObservable.title)
Text(articleLoader.realData ?? "Not found")
HStack {
Spacer()
Button(action: {
if id > 1 {
id = id - 1
articleLoader.loadItemForID(id: id)
}
}) {
Text("<-")
}
Button(action: {
if id < articleLoader.items.count {
id = id + 1
articleLoader.loadItemForID(id: id)
}
}) {
Text("->")
}
Button(action: {
anotherObservable.title = "Another Title"
}) {
Text("Change title")
}
Spacer()
}
}
.onAppear { articleLoader.loadItemForID(id: id)}
}
}
The issue is that you're calling loadItemForID
in onAppear
only:
.onAppear { articleLoader.loadItemForID(id: id)}
And when you change title, the above function is not called again.
The easiest solution is probably to store DemoArticleLoader()
as a @StateObject
, so it's not recreated every time:
struct Class1: View {
@StateObject var anotherObservable = AnotherObservable()
@StateObject var itemsLoader = DemoItemsLoader()
@StateObject var articleLoader = DemoArticleLoader() // create once
var body: some View {
NavigationView {
VStack {
ForEach(itemsLoader.items, id: \.self) { item in
NavigationLink(
// pass here
destination: Class2(anotherObservable: anotherObservable, articleLoader: articleLoader, id: item)) {
Text("\(item)")
}
}
}
}
}
}