I have a problem with implementing some logical simply code. In simple terms I want to make something like this:
Complete command() {
if (error) {
show error alert
}
else {
show "Happy ending" allert
}
}
I know how to make first part, but with second I have a problem
My try:
[self.vm.applyCommand.errors subscribeNext:^(id x) {
//Show alert with error
}];
[[[self.vm.applyCommand.executionSignals
map:^(RACSignal *signal) {
return [[signal ignoreValues] materialize];
}]
concat]
subscribeNext:^(RACEvent *event) {
//Show alert with "Happy end"
}];
In this case everything works as expected until error appears. In case of error I see two alerts (one with error, another with "Happy end"), but want only one with error.
I know why it happens,
Errors will be automatically caught upon the inner signals ...etc
but I want some solution to make desirable behavior.
UPD: "...see two alerts (one with error, another with "Happy end")"
There are two possible ways to achieve what you want, depending on how the execution signal for applyCommand
behaves.
If it sends only one next
value and then immediately completes, you can simply use switchToLatest
operator to subscribe for that next
value and display the alert with 'Happy end':
[[self.command.executionSignals switchToLatest] subscribeNext:^(id x) {
//Show alert with "Happy end"
}];
Otherwise it's much more complicated, because it's hard to distinguish between successful completion and failure of RACCommand
's execution signal. In case of error you get a "completed" RACEvent
when calling [[signal ignoreValues] materialize];
, and then command's errors
signal sends the error as its next value.
I managed to do it using command's executing
signal, which sends @NO
after the errors
signal sends the error. You can use merge
and combinePreviousWithStart:reduce
operators to check if the command stopped executing because an error occurred:
RACSignal *stoppedExecuting = [[self.vm.applyCommand.executing ignore:@YES] skip:1];
RACSignal *merged = [stoppedExecuting merge:self.vm.applyCommand.errors];
[[[merged combinePreviousWithStart:nil reduce:^id(id previous, id current) {
return @( [previous isKindOfClass:[NSError class]] || [current isKindOfClass:[NSError class]] );
}] filter:^BOOL(NSNumber *errorOccurred) {
return !errorOccurred.boolValue;
}] subscribeNext:^(id x) {
NSLog(@"Happy end!");
}];
It's not a perfect solution as it depends on order of sending values from various RACCommand
's signals, which is an implementation detail and can change in the future (it worked for me with RAC 2.5).
I suppose that this problem might be solved with RAC 3.0, as it replaces RACCommand
with Action
, but I haven't tried it yet.