Reputation: 7163
ReactiveCocoa 4 has a method on the SignalProducer
class, then
that waits for the producer to completed and replaces it with another producer like so:
someProducer.then(replacementProducer)
.
However what I want is a method that waits until the original producer errors out and then replaces it with the second producer (doing nothing on completion events).
then
is implemented as (note next events are not forwarded):
public func then<U>(replacement: SignalProducer<U, Error>) -> SignalProducer<U, Error> {
let relay = SignalProducer<U, Error> { observer, observerDisposable in
self.startWithSignal { signal, signalDisposable in
observerDisposable.addDisposable(signalDisposable)
signal.observe { event in
switch event {
case let .Failed(error):
observer.sendFailed(error)
case .Completed:
observer.sendCompleted()
case .Interrupted:
observer.sendInterrupted()
case .Next:
break
}
}
}
}
return relay.concat(replacement)
}
What I want would look similar except on failure, instead of sending an error, it would send a completed signal. More specifically the switch statement would change to look like:
signal.observe { event in
switch event {
case .Failed:
observer.sendCompleted()
case .Completed, .Next: break
case .Interrupted:
observer.sendInterrupted()
}
}
}
My questions are as follows:
1) Is this valid? It works as I would expect, the replacement signal is only started when the first signal errors out. 2) Is there a better way to do it so that the error information doesn't get lost?
An example of where I would use this is a two-step authentication process:
self.verifyToken(token).failed(self.revalidateSession())
If the token verification succeeds, all is good and no need to revalidate. If it fails, then revalidation needs to occur.
EDIT:
I changed the signal observation block so that if the first signal completes with no error, it starts the replacement signal with startWithComplete
so that completion events are forwarded down the chain without triggering any action from the signal.
That makes it easy to do this:
self.verify(token).failed(self.refreshSession(token)).then(self.fetchUserForToken(token))
where then
is chained and only gets executed when either verify
or refreshSession
is successful.
signal.observe { event in
switch event {
case .Failed:
observer.sendCompleted()
case .Completed:
replacement.startWithCompleted {}
case .Next: break
case .Interrupted:
observer.sendInterrupted()
}
}
}
Upvotes: 3
Views: 487
Reputation: 4987
1) Is this valid?
No. If your verifying signal producer sends a normal value and completes, your replacement signal producer does start (by the startWithCompleted
call) but will produce nothing (since you are not observing anything in the startWithCompleted
closure, and the inner replay
signal producer will never terminate).
2) Is there a better way?
You can use flatMapError
operator, this way you can also get the error information.
self.verify(token)
.map { _ in () }
.flatMapError { error in
return self.refreshSession(token).map { _ in () }
}
Note, The two map { _ in () }
can be omitted if the veriy
producer and refreshSession
producer produce values of the same type.
Upvotes: 1