3

I am new to ReactiveCocoa, but I think it's very nice and outstanding technique for reducing code complexity. I just started experiencing with the framework, and not everything is clear for me at the moment, so excuse me if my problem can be solved in some obvious way.

In my app I have login view controller with simple form contains two text fields (username and password) and a button. I would like the button to be disabled if any of two text fields is empty. So, I wrote this code:

RAC(self.loginButton, enabled) = 
[RACSignal combineLatest:@[self.userTextField.rac_textSignal,
                           self.passwordTextField.rac_textSignal]
                  reduce:^(NSString *username,
                           NSString *password) {
                      BOOL valid = (username.length > 0 && password.length > 0);
                      return @(valid);
                  }];

It's very simple and it's working. The problem is that one of my text fields (the password field) has secureTextEntry and clearsOnBeginEditing properties set to YES. I will try to explain unwanted behavior that I am experiencing with this configuration:

Let's assume that both username and password fields are NOT empty. In this case the button is enabled. When user taps on password field, it becomes first responder (keyboard appears and user can enter his password), but because of clearsOnBeginEditing being set to YES for that field, the previously entered password is cleared from the text field. That's way password field is now empty. The problem is that signal is not being sent, so the button remains enabled, despite the password field is empty.

My first idea to solve this issue (well, more like workaround solution) was to observe isFirstResponder property on password field beside observing text changes. That's way the block that checks if button should be enabled would be called when password field becomes first responder. I don't know if this solution works, because I have no idea how to implement it using ReactiveCocoa. I have looking for creating a signal for isFirstResponder property changes, but without a luck. It might be not the best approach in order to solve this issue, but nothing comes to my mind at this point.

Then, the question is: how to observe isFirstResponder property with ReactiveCocoa?

And more general question: how to observe text field's text changes when clearsOnBeginEditing is set to YES?

UPDATE:

I found out that I can create signal for UIControlEventEditingDidBegin event that should give me substitution of observing isFirstResponder property changes:

[self.passwordTextField rac_signalForControlEvents:UIControlEventEditingDidBegin]

Unfortunately this does not solve the issue. Now I understand that field is cleared AFTER it becomes first responder, and clearing field automatically after it becomes first responder does not send signal for text changes. That's way when validation block is executed it still thinks that password field is not empty, and the button remains enabled despite password field was cleared and it's empty.

Darrarski
  • 3,882
  • 6
  • 37
  • 59

1 Answers1

3

Unfortunately the -rac_textSignal only listens for UIControlEventEditingChanged. If UIControlEventEditingDidBegin were added, you'd be all set.

I suppose you could patch this into it and submit a pull request?

- (RACSignal *)rac_textSignal {
    @weakify(self);
    return [[[[[RACSignal
        defer:^{
            @strongify(self);
            return [RACSignal return:self];
        }]
        concat:[self rac_signalForControlEvents:UIControlEventEditingChanged|UIControlEventEditingDidBegin]]
        map:^(UITextField *x) {
            return x.text;
        }]
        takeUntil:self.rac_willDeallocSignal]
        setNameWithFormat:@"%@ -rac_textSignal", [self rac_description]];
}
drhr
  • 2,261
  • 2
  • 17
  • 35
  • 1
    It's by design that only text changes are observed on the text property. Such pull requests have been discussed earlier but there is no intention to do it that way. Look at my issue for more detail: https://github.com/ReactiveCocoa/ReactiveCocoa/issues/516 – allprog Feb 07 '14 at 08:29
  • This won't work, field is cleared AFTER it becomes first responder, and clearing field automatically after it becomes first responder does not send signal for text changes. First I will be notified that editing did begin, then (later) field will be cleared. – Darrarski Feb 07 '14 at 09:59
  • Won't work? Have you tried it? I tried it and it appears to work. It looks like the "clear when editing begins" option causes the text value to update before the `UIControlEventEditingDidBegin` event fires, for some reason. If you log the values you'll see that that field is empty. – drhr Feb 07 '14 at 17:51
  • @allprog I'm not sure that that issue is the same thing. You're talking about receiving the `-rac_textSignal` after programmatic updates rather than control events. All I'm suggesting here is that one additional control event be considered. The whole point of this is to properly get the updated value when it's needed - I would consider "clear when editing begins" to be on par with actual text changes driven by the user and their keyboard. It's as if the user deleted the whole field with backspace. – drhr Feb 07 '14 at 17:59
  • @Darraski I tried it two ways, one with the above method, and one by just adding a separate signal for `UIControlEventEditingDidBegin`. Both seem to work. – drhr Feb 07 '14 at 18:11
  • You can open the pull request but I think the Github guys have reasons why this is not added. – allprog Feb 07 '14 at 19:50
  • Went ahead and submitted a PR. It seems like a valid thing to have here, but we'll see what they say. – drhr Feb 07 '14 at 21:11
  • 3
    Hey guys, just FYI in case you need it, this was merged into the 2.0 master. – drhr Feb 14 '14 at 01:06