0

In the example below, "2" will never be printed, since the error is a completion event, stopping the publisher from sending any more events. That's very clear to me.

import Combine

enum TestError: Error {
  case ohnoes
}

let publisher = PassthroughSubject<Int, Error>()

publisher
  .sink(receiveCompletion: { completion in
  print(completion)
}) { int in
  print(int)
}

publisher.send(1)
publisher.send(completion: .failure(TestError.ohnoes))
publisher.send(2)

Sadly, even replaceError and catch won't stop the completion event, so the pipeline will still end.

So my question is, how can I replace the error with a nil value and prevent the completion event? Basically I want to transform AnyPublisher<Int, Error> into AnyPublisher<Int?, Never>, for those cases where I simply don't care about the error at all, and just want to keep receiving future values.

With ReactiveKit I had this extension:

extension Signal {
  func errorToOptional() -> Signal<Element?, Never> {
    return Signal<Element?, Never> { observer in
      self.observe { event in
        switch event {
        case .next(let element):
          observer.receive(element)
        case .failed:
          observer.receive(nil)
        case .completed:
          observer.receive(completion: .finished)
        }
      }
    }
  }
}

And it worked great. But the Combine types make creating extensions like these very difficult (if not impossible?).

Kevin Renskers
  • 5,156
  • 4
  • 47
  • 95
  • 1
    If a publisher sends a failure, it is dead by definition. That is what failure means in Combine. You cannot do anything downstream of the publisher that will magically change that. – matt Feb 13 '20 at 23:35
  • Damn. And yeah I just noticed that the ReactiveKit extension also didn't do what I kinda expected it to do. I guess changing the publisher/signal itself to have a `Never` error type and a `Result` output type might be best to prevent certain publishers from dying on errors. – Kevin Renskers Feb 14 '20 at 00:01

1 Answers1

1

You said

publisher.send(completion: .failure(TestError.ohnoes))

The publisher therefore emits a failure. That is what you told it to do. If a publisher emits a failure, it is dead by definition. That is what failure means in Combine. You cannot do anything downstream of the publisher that will magically change that.

So my question is, how can I replace the error with a nil value and prevent the completion event?

You can prevent the error (failure) from reaching the sink by putting something between the publisher and the sink that catches the error or transforms it into Never. But you cannot prevent the publisher from failing because that is what you just told it to do.

Basically I want to transform AnyPublisher into AnyPublisher, for those cases where I simply don't care about the error at all, and just want to keep receiving future values.

There are not going to be any future values coming from this publisher. It has failed. That is what emitting an error means. And that is what you told the publisher to do.

matt
  • 515,959
  • 87
  • 875
  • 1,141