So I am working on a view in SwiftUI which will update its state when an event is published.
The view looks like this:
struct MyView: View {
@EnvironmentObject var dataSource: DataSource
@State var data: [Model] = []
func refreshData() {
self.data = dataSource.getData()
}
var body: some View {
VStack {
List(self.data) { model in
Row(model: model)
}
}
.onAppear {
self.refreshData()
}
.onReceive(self.dataSource.didUpdate) { _ in
print("on receive")
self.refreshData()
}
}
}
class DataSource: ObservableObject {
var didUpdate: PassthroughSubject<Model,Never> ...
}
So with this setup, the onAppear
block is called and works as expected. But the onReceive
callback is never called. I have been able to verify that .send
is being called correctly on the DataSource.didUpdate
subject, but it appears the subscriber is not being notified.
Is there something I am missing to make this work?
CodePudding user response:
For the subscriber to be notified, it needs to listen to the "binding" rather than the variable itself. Changing it to
.onReceive(self.dataSource.$didUpdate)
should fix the issue. You can read up more here.
CodePudding user response:
As you are correctly declaring your DataSource
as an observable object class, what you need now is to use the @Published
property wrapper on you didUpdate
variable. Then, with SwiftUI you can listen to it using .onChange(of:) { }
. Just note that it does not work with computed vars: in such case, use the computed var to update the published var. Also, I assume you are correctly injecting your model instance in the environment (otherwise it will never work).
Like this:
struct MyView: View {
@EnvironmentObject var dataSource: DataSource // Remember to inject the specific instance in the environment
@State var data: [Model] = []
func refreshData() {
self.data = dataSource.getData()
}
var body: some View {
VStack {
List(self.data) { model in
Row(model: model)
}
}
.onAppear {
self.refreshData()
}
// .onReceive(self.dataSource.didUpdate) { _ in
// print("on receive")
.onChange(of: dataSource.didUpdate) { _ in // Listen to changes in ObservableObject property that publishes
print("on change")
self.refreshData()
}
}
}
class DataSource: ObservableObject {
// Note the property wrapper, but it does not work with computed vars:
// in such case, use the computed var to update the published var.
// Also: update it only in the main thread!
@Published var didUpdate: PassthroughSubject<Model,Never> ...
}