erotsppa
erotsppa

Reputation: 15061

How to write publisher that conditionally publish?

Lets say I have a publisher like this:

NotificationCenter.default.publisher(for: NSNotification.Name.NSManagedObjectContextObjectsDidChange)
  .map { notification in /.. do something ../}

how do I make it so that in my block, I stop the publisher from emitting?

I could make it a trymap and then throw an error. But it's so weird to have to throw just to indicate a lack of results (in this case, let's say a non relevant NSManagedObject change)

Upvotes: 1

Views: 2233

Answers (3)

matt
matt

Reputation: 535944

Another way is to use flatMap and emit an Empty as the blocker (and a Just otherwise):

NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange)
    .flatMap { input -> AnyPublisher<Notification, Never> in
        if somethingOrOther {
            return Just(input).eraseToAnyPublisher()
        } else {
            return Empty().eraseToAnyPublisher()
        }
    }

Upvotes: 1

rob mayoff
rob mayoff

Reputation: 385998

One way is to use the filter operator. This passes the original input downstream if the predicate is true:

NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange)
  .filter { note in note.object === myContext }
  .map { note in /.. do something ../}

Another way (mentioned by bscothern in a comment) is to use compactMap. You can transform the input into a different output, or into nil if want to suppress output:

NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange)
  .compactMap { note in note.object === myContext ? myContext : nil }
  .map { context in /.. do something ../}

Upvotes: 3

Asperi
Asperi

Reputation: 258413

Here is possible approach based on connectable publisher (the demo is based on SwiftUI). Tested with Xcode 11.3.1 / iOS 13.3

demo

struct TestConnectableNotification: View {

    let publisher = NotificationCenter.default.publisher(for: Notification.Name("test")).makeConnectable()

    @State private var text = "<none>"
    @State private var subscriber: Cancellable? = nil
    var body: some View {
        VStack {
            Text("Current: \(text)")
                .onReceive(publisher) { _ in
                    let value = Int.random(in: 0..<10)
                    self.text = "\(value)"
                }
            Button(self.subscriber == nil ? "Enable" : "Disable") {
                if nil == self.subscriber {
                    self.subscriber = self.publisher.connect()
                } else {
                    self.subscriber?.cancel()
                    self.subscriber = nil
                }
            }
            Button("Post") {
                NotificationCenter.default.post(name: NSNotification.Name("test"), object: nil)
            }
        }
    }
}

Upvotes: 1

Related Questions