Home > Mobile >  onReceive callback not executing
onReceive callback not executing

Time:02-28

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> ...
}

  • Related