如何编写有条件发布的发布者?

How to write publisher that conditionally publish?

假设我有这样的发布商:

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

如何才能在我的区块中阻止发布者发布?

我可以将其设为 trymap,然后抛出错误。但是不得不抛出只是为了表明缺少结果真是太奇怪了(在这种情况下,假设是一个不相关的 NSManagedObject 更改)

这是基于可连接发布者的可能方法(该演示基于 SwiftUI)。使用 Xcode 11.3.1 / iOS 13.3

测试

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

另一种方法是使用 flatMap 并发出 Empty 作为拦截器(否则为 Just):

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

一种方法是使用 filter 运算符。如果谓词为真,这会将原始输入传递到下游:

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

另一种方法(bscothern 在评论中提到)是使用 compactMap。您可以将输入转换为不同的输出,或者如果想抑制输出则转换为 nil:

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