6

I'm trying to understand how to dispose of a RACSignal that is scheduled to run on a background thread.

// Start button
@weakify(self);
[[self.startButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *sender) {
    @strongify(self);
    self.startButton.enabled = NO;
    NSDate *startDate = [NSDate date];
    RAC(self, elapsedTime) = [[[[RACSignal interval:0.1f onScheduler:
                                 [RACScheduler schedulerWithPriority:RACSchedulerPriorityDefault]]
                                startWith:[NSDate date]] map:^id(id value) {
        NSTimeInterval timeInterval = [(NSDate *)value timeIntervalSinceDate:startDate];
        return [NSNumber numberWithDouble:timeInterval];
    }] deliverOn:[RACScheduler mainThreadScheduler]];
}];

// Stop button
[[self.stopButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
    self.startButton.enabled = YES;

    // How do I stop the timer here...???

}];

RAC(self.timeLabel, text) = [RACObserve(self, elapsedTime) map:^id(NSNumber *number) {
    NSString *string = [NSString stringWithFormat:@"%.1f sec. elapsed", number.floatValue];
    return string;
}];

The above code does the following:

  • bind a RACCommand to a button that starts a RACSignal
  • the RACSignal is bound to an NSNumber (elapsedTime) that is sent a new value every 0.1 sec.
  • finally I have a timeLabel bound to the number that let's me display on screen a timer updated every 0.1 seconds.

What I would like to do is being able to start and stop the timer when clicking the START and STOP buttons. Problem is I don't understand how to dispose of the signal.

Mike
  • 63
  • 1
  • 7

2 Answers2

8

Usually when you want to stop a signal from continuing to send events, you'll want to use the takeUntil: operator. This is just a crude example, and could probably use a little more finessing, but this should work:

@weakify(self);
[[self.startButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *sender) {
    @strongify(self);
    self.startButton.enabled = NO;
    NSDate *startDate = [NSDate date];
    RAC(self, elapsedTime) = [[[[[RACSignal interval:0.1f onScheduler:
                                 [RACScheduler schedulerWithPriority:RACSchedulerPriorityDefault]]
                                startWith:[NSDate date]] 
                                takeUntil:[self.stopButton rac_signalForControlEvents:UIControlEventTouchUpInside]] map:^id(id value) {
        NSTimeInterval timeInterval = [(NSDate *)value timeIntervalSinceDate:startDate];
        return [NSNumber numberWithDouble:timeInterval];
    }] deliverOn:[RACScheduler mainThreadScheduler]];
}];
Ash Furrow
  • 12,391
  • 3
  • 57
  • 92
  • 7
    Nested subscriptions—implicitly done with the `RAC()` binding here—are a pretty big anti-pattern. They can usually be replaced with signal operators and pulling work out to the top level. In this case, each touch event should generate a new interval signal (which is already happening), then they should be joined using `-switchToLatest` (to only honor the latest), and finally the result bound to `elapsedTime` in one signal chain. – Justin Spahr-Summers Jan 22 '14 at 08:48
1

Just using takeWhileBlock and setting a flag like shouldReapeatSignalActive

takeWhileBlock:^BOOL(id x) {
        @strongify(self);
        return shouldReapeatSignalActive;
}

when shouldReapeatSignalActive set to NO, all subscribers will all unsubscribe the signal anymore (unless you resubscribe the signal).

Blakes Seven
  • 49,422
  • 14
  • 129
  • 135
user3044484
  • 463
  • 5
  • 7