Reputation: 557
I have a situation where I want to split events from a specific origin signal into two signals, one emitting events immediately and another emitting events with a 3 seconds delay. And rest of the origin events always emit a new event immediately. But if a new event arrives on the origin signal before the delayed event has been emitted, the delayed event should be discarded.
I map each the origin signal to CS1(for immediately emitted events), and filter specific events of the origin signal to CS2(for delayed emitted events).
Then I merge CS1 and CS2 into CS3 which will not discard S2.
So, my question is how to discard or cancel S2, and how can I achieve this situation using RAC without using extra temporary variables?
ReactiveCocoa 2.x current code
RACSignal* origin = …
RACSignal* CS1 = [origin map:^id _Nullable(id _Nullable value) {
return @(YES);
}];
RACSignal* CS2 = [[[origin filter:^BOOL(id _Nullable value) {
return [RACSignal empty];
}] delay:3] map:^id _Nullable(id _Nullable value) {
return @(NO);
}];
RACSignal* CS3 = [RACSignal merge:@[CS1, CS2]];
Upvotes: 0
Views: 88
Reputation: 3357
One remark unrelated to the actual question: filter
should return a bool that is YES
if the element should be sent and NO
if the element should be filtered out.
To the actual question:
The solution to the problem is to use takeUntil
. However, if you apply takeUntil
directly to CS2, CS2 as a whole will be cancelled as soon as an event arrives on CS1.
The solution is to use flatMap
, to build a new RACSignal
for the delayed element and then use takeUntil
on that inner signal.
I've split the single steps into multiple temporary signals only for clarity (I've also changed the map
and filter
so I could see better whats happening when trying my example, you should easily be able to use your correct functions there):
RACSignal* CS1 = [self.origin map:^id _Nullable(NSNumber * _Nullable value) {
return value;
}];
RACSignal *filtered = [self.origin filter:^BOOL(NSNumber * _Nullable value) {
return (value.integerValue % 2) == 0;
}];
RACSignal *delayed = [filtered flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) {
// Build a new signal that returns just this one value,
// but delayed and only if no event arrives on CS1
return [[[RACSignal return:value]
delay:3]
takeUntil:CS1];
}];
RACSignal* CS2 = [delayed map:^id _Nullable(NSNumber * _Nullable value) {
return @(-value.integerValue);
}] ;
RACSignal* CS3 = [RACSignal merge:@[CS1, CS2]];
You can easily collapse this back into just two signals
RACSignal *CS2 = [[[self.origin filter:^BOOL(NSNumber * _Nullable value) {
return (value.integerValue % 2) == 0;
}] flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) {
// Build a new signal that returns just this one value,
// but delayed and only if no event arrives on CS1 before
return [[[RACSignal return:value]
delay:3]
takeUntil:CS1];
}] map:^id _Nullable(NSNumber * _Nullable value) {
return @(-value.integerValue);
}];
I've created a sample project on github to demonstrate the solution.
Upvotes: 1