Reputation: 1679
In this specific case, when I try to change an @EnvironmentObject
's @Published var
, I find that the view is not invalidated and updated immediately. Instead, the change to the variable is only reflected after navigating away from the modal and coming back.
import SwiftUI
final class UserData: NSObject, ObservableObject {
@Published var changeView: Bool = false
}
struct MasterView: View {
@EnvironmentObject var userData: UserData
@State var showModal: Bool = false
var body: some View {
Button(action: { self.showModal.toggle() }) {
Text("Open Modal")
}.sheet(isPresented: $showModal, content: {
Modal(showModal: self.$showModal)
.environmentObject(self.userData)
} )
}
}
struct Modal: View {
@EnvironmentObject var userData: UserData
@Binding var showModal: Bool
var body: some View {
VStack {
if userData.changeView {
Text("The view has changed")
} else {
Button(action: { self.userData.changeView.toggle() }) {
Text("Change View")
}
}
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
MasterView().environmentObject(UserData())
}
}
#endif
Is this a bug or am I doing something wrong?
This works if changeView
is a @State var
inside Modal. It also works if it's a @State var
inside MasterView
with a @Binding var
inside Modal
. It just doesn't work with this setup.
Upvotes: 13
Views: 5797
Reputation: 1332
In Xcode 11 GM2, If you have overridden objectWillChange
, then it needs to call send()
on setter of a published variable.
If you don't overridden objectWillChange
, once the published variables in @EnvironmentObject or @ObservedObject change, the view should be refreshed. Since in Xcode 11 GM2 objectWillChange
already has a default instance, it is no longer necessary to provide it in the ObservableObject
.
Upvotes: 1
Reputation: 5348
Changing
final class UserData: NSObject, ObservableObject {
to
final class UserData: ObservableObject {
does fix the issue in Xcode11 Beta6. SwiftUI does seem to not handle NSObject
subclasses implementing ObservableObject
correctly (at least it doesn't not call it's internal willSet
blocks it seems).
Upvotes: 4
Reputation:
A couple of things.
MasterView
either.import Combine
in your code (don't worry, that alone doesn't help).Here's the fix. I don't know if this is a bug, or just poor documentation - IIRC it states that objectWillChange
is implicit.
Along with adding import Combine
to your code, change your UserData
to this:
final class UserData: NSObject, ObservableObject {
var objectWillChange = PassthroughSubject<Void, Never>()
@Published var changeView: Bool = false {
willSet {
objectWillChange.send()
}
}
}
I tested things and it works.
Upvotes: 8