Ash Furrow
Ash Furrow

Reputation: 12421

Why Does RACCommand's block return a signal?

I've been learning a lot about ReactiveCocoa but one thing still puzzles me: why does the signal block on RACCommand return a signal itself?

I understand the use cases of RACCommand, its canExecute signal and signal block, and how it can be hooked up to UI elements. But what case would there be ever for returning something other than [RACSignal empty]?

infoButton.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
    // Do stuff

    return [RACSignal empty];
}];

Upvotes: 26

Views: 5108

Answers (4)

Nikolay Kasyanov
Nikolay Kasyanov

Reputation: 947

Imagine you have a command that should load list of items from network. You could use side effects in signal block or return a signal that would actually send these items. In the latter case you can do the following:

RAC(self, items) = [loadItems.executionSignals switchToLatest];

Also all errors sent by signal would be redirected to errors signal, so:

[self rac_liftSelector:@selector(displayError:) 
           withSignals:loadItems.errors, nil];

It's impossible with [RACSignal empty]-powered commands.

Upvotes: 7

Dave Lee
Dave Lee

Reputation: 6489

There are exceptions to every rule, but generally you want all your "// Do stuff" to be captured by the returned signal. In other words, your example would be better as:

infoButton.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id input) {
    return [RACSignal defer:^{
        // Do stuff

        return [RACSignal empty];
    }];
}];

The direct benefit of this change is that, for the duration of "// Do stuff", your infoButton will be disabled, preventing it from being clicked/tapped until the returned signal has completed. In your original code, the "do stuff" is outside of the signal, and as such your button won't be disabled properly.

For work that doesn't have much latency, for example making UI changes in response to a button tap, then the enabled/disabled feature of RACCommand doesn't buy you much. But if the work is a network request, or some other potentially long running work (media processing for example), then you definitely want all of that work captured within a signal.

Upvotes: 28

babygau
babygau

Reputation: 1581

Instead of using this:

infoButton.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id input) {
    return [RACSignal defer:^{
        // Do stuff

        return nil;
    }];
}];

You can use this built-in method to control the button state:

- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock;

Upvotes: 2

terry lewis
terry lewis

Reputation: 672

I have an example that might be helpful, though others might may be able to explain it better. RACCommand Example

But basically, the way you have it with returning +empty it seems sort of pointless, as invoking the command will basically be using side effects, which we want to avoid.

Upvotes: 6

Related Questions