Reputation: 1223
I recently came across an issue where I didn't find a way to work around it. If I place an observable object inside an environment object, my views don't update when a variable is changing. This is my setup:
inside SceneDelegate I decalre the parent view with environmentObject:
let parent = ParentView().environmentObject(Data())
This is the environment Object:
class Data: ObservableObject {
enum Page {
case pageOne
}
var page: Page = .pageOne
var person = Person()
}
now all my views are linked to the parent view:
struct ParentView: View {
@EnvironmentObject var data: Data
var body: some View {
VStack {
if self.data.page == .pageOne {
PageOne()
} else {
Text("No pages")
}
}
}
}
I have a class (I called it "Person"):
import Foundation
import SwiftUI
import Combine
class Person: ObservableObject {
let objectWillChange = PassthroughSubject<Person,Never>()
@Published var age = 30 { didSet { objectWillChange.send(self)} }
func birthday() {
self.age += 1
print(age)
}
}
And this is where my problem occurs:
struct PageOne: View {
@EnvironmentObject var data: Data
var body: some View {
Text("\(data.person.age)").onTapGesture {
self.data.person.birthday()
}
}
}
When I tap the text, the age of the person is increasing but the view does not update. I think I don't even understand why this is happening. I thought I knew how it works but apparently I don't.
Upvotes: 4
Views: 2072
Reputation: 54426
It looks like in your case Person
doesn't have to be a class.
You can try this instead:
class Data: ObservableObject {
enum Page {
case pageOne
}
var page: Page = .pageOne
@Published var person = Person() {
didSet {
objectWillChange.send()
}
}
}
struct Person {
var age = 30
mutating func birthday() {
self.age += 1
print(age)
}
}
If you need Person
to remain a class you can make it @Published
:
class Data: ObservableObject {
enum Page {
case pageOne
}
var page: Page = .pageOne
@Published var person = Person() // <- add `@Published`
}
and remove unnecessary objectWillChange
code from Person
:
class Person: ObservableObject {
@Published var age = 30
func birthday() {
self.age += 1
print(age)
}
}
Then use Person
as an @ObservedObject
in the PageOne
view:
struct ContentView: View {
@EnvironmentObject var data: Data
var body: some View {
VStack {
if self.data.page == .pageOne {
PageOne(person: data.person) // pass `person` object
} else {
Text("No pages")
}
}
}
}
struct PageOne: View {
@ObservedObject var person: Person // observe `person` directly
var body: some View {
Text("\(person.age)").onTapGesture {
self.person.birthday()
}
}
}
Upvotes: 2