Joan Romano
Joan Romano

Reputation: 45

Combining signals but just subscribe when first one changes

I want to combine the rac_signalForControlEvent on a UIButton with some combined textFields signals like so:

    [[[[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside]
    combineLatestWith:textFieldsCombinedSignal]
    filter:^BOOL(RACTuple *signals) {
      return ((UIButton *)[signals first]).highlighted;
    }]  subscribeNext:^(RACTuple *signals) {
      if ([signals.second boolValue])
      {
          [self doLogin];
      }
      else
      {
          [self error];
      }
    }];

But this way I have to filter for the button highlighted state otherwise the subscribeNext: block is getting fired every time some of the textfields change (textFieldsCombinedSignal).

I would love to achieve this without having to filter for the highlighted button's state (I'm using ReactiveCocoa to minimize state after all, and I don't feel like this is the proper way to do what I'm trying to do).

Upvotes: 0

Views: 454

Answers (2)

Dave Lee
Dave Lee

Reputation: 6489

If you want a sequence of button tap and then the latest from your combined text field signal, it can be as simple as using -flattenMap::

[[[self.loginButton
    rac_signalForControlEvents:UIControlEventTouchUpInside]
    flattenMap:^(id _) {
        return [textFieldsCombinedSignal take:1];
    }]
    subscribeNext:^…];

However, from what you've described, this seems like a classic case for RACCommand. Both this code, and the original code, allow for the user to double tap the login button and trigger concurrent logins.

Before showing RACCommand, I'll make a couple of assumptions about your code. From the name alone, textFieldsCombinedSignal it could be a signal that sends a tuple of strings, but in your use it looks like it's actually a validation signal that sends YES/NO. I'll assume the latter and rename it to loginIsValid. I'll also assume that -doLogin is synchronous for the purpose of this example.

Now to RACCommand:

self.loginButton.rac_command = [[RACCommand alloc] initWithEnabledSignal:loginIsValid signalBlock:^(id _) {
    return [RACSignal defer:^{
        [self doLogin];
        return [RACSignal empty];
    }];
}];

What this will do is enable/disable the login button based on the latest value sent on loginIsValid. When enabled, a tap on the button will cause -doLogin to be called, and the button will become disabled for the duration of the login process, preventing concurrent logins.

Upvotes: 1

erikprice
erikprice

Reputation: 6308

Sounds like you just want to sample the textFieldsCombinedSignal whenever the button is tapped. Try using -sample::

RACSignal *buttonSignal = [self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside];
[[textFieldsCombinedSignal sample:buttonSignal] subscribeNext:^(RACTuple *combinedTextFields) {
    // do stuff with combinedTextFields
}];

Upvotes: 1

Related Questions