0

I am trying to work with the below scenario I have two producers A and B. producerB should execute only when producerA is executed successfully and also if producerA throws error, handle the error and stop there. So I tried something like this.

producerA.flatMapError {
    // handle error and stop right here 
}.then(producerB).startWithResult { 
    // ...
}

Looks like producerB executes even if producerA throws error. Please help me how can I make it work with my scenario.

Ferschae Naej
  • 1,826
  • 1
  • 15
  • 16
coolly
  • 343
  • 1
  • 6
  • 16

2 Answers2

1

The question is what, exactly, you mean with "does not throw an error".

The sequence of Events on a Signal/SignalProducer has a precisely defined semantic

Theres an arbitrary number of Value (from 0 - x) Events, followed, eventually, by a completed, failed or interrupted Event. After that there's no more events.

Generally you can say that most operators only operate on value events and immediately propagate failed events (without operating on them). If you're not sure in for a specific operator, take a look at the documentation of that operator which is very clear about the behaviour for failure events.


So one way to understand the question is to say when producerA completes successfully (after an arbitrary number of value Events), then start producerB and if producerA sends a failed Event, then don't.

In that case, the then operator is exactly what you need. It will start producerB as soon as producerA completes, but not if producerA fails!

producerA.then(producerB)
  .start(Signal.Observer(value: { value in
    print("Value \(value)")
  }, failed: {error in
    print("Error \(error)")
  }))

Note, that you dont want to use the flatMapError here because that would (depending on how your error handling in the block looks) convert failed events to value Events which would trigger producerB eventually.


Another way to understand the question is to say each event on producerA that is not an error should trigger producerB once

In that case, you would use flatMap on the events of producerA to return a producerB for each event on producerA. Note here, again, flatMap propagates a failed event immediately, so a failed event on producerA will cause the whole chain to fail without execution of producerB

producerA.flatMap(.concat) { _ in return producerB }
  .start(Signal.Observer(value: { value in
    print("Value \(value)")
  }, failed: {error in
    print("Error \(error)")
  }))
MeXx
  • 3,357
  • 24
  • 39
  • 1
    Good answer. I find that a good way to think about it is that `flatMapError` is basically used to catch an error. In fact, it [used to be called catch](https://github.com/ReactiveCocoa/ReactiveCocoa/commit/e0c266320ba160944eda894210ece7121216f174). So you shouldn't use it if you actually want the error to terminate the signal chain. I would add that if you don't want the entire chain terminated on error then you can just put the `flatMapError` *after* `then.(producerB)`. – jjoelson Oct 05 '17 at 12:29
  • @MeXx i see some problem here in the first approach. Let's say first producer doesn't producer error and second producer produces the error, it is printing the value and it never goes to failed block. – coolly Oct 05 '17 at 13:03
  • @coolly, after `producerA` sends its value, is it completing? `producerB` will only start once `producerA` completes. You should use the second approach with `flatMap` if you want `producerB` to start when `producerA` sends a value. – jjoelson Oct 05 '17 at 13:08
0

try this:

func scenario() -> SignalProducer<MyValueType, MyErrorType> {
        return SignalProducer { observer, _ in
            producerA.startWithResult({ (res) in
                switch res {
                case .success(let value):
                    observer.send(value: value)
                    observer.sendCompleted()
                case .failure(let error):
                    print(error)//handle your error here
                    observer.send(error: err)
                }
            })

        }.then(producerB)
    }

scenario().start()

You create a custom SignalProducer and define the desired behaviour by telling the observer to operate based on your scenario.

Alchi
  • 799
  • 9
  • 19