Okay, two things:
You can use the and
helper of RACSignal
to make that signal reduction a little cleaner:
RACSignal *signUpActiveSignal = [[RACSignal combineLatest:@[validEmailSignal,
validPasswordSignal,
validConfirmPasswordSignal]] and];
You can also use the RAC
macro to bind the enabled
property (the only reason that the example code is using subscribeNext
instead of RAC
is that CGColorRef
isn't of type id
):
RAC(self.signUpButton, enabled) = signUpActiveSignal;
Now onto the reason this isn't working. Let's look at the calculations for the password fields:
RACSignal *validPasswordSignal = [passwordTextField.rac_textSignal map:^id(NSString *text) {
return @([text isEqualToString:self.confirmPasswordTextField.text] && text.length > 5);
}];
RACSignal *validConfirmPasswordSignal = [confirmPasswordTextField.rac_textSignal map:^id(NSString *text) {
return @([text isEqualToString:self.passwordTextField.text] && text.length > 5);
}];
Every time passwordTextField
's text changes, it compares it to confirmPasswordTextField
. But it only checks that they're the same when passwordTextField
changes, not when confirmPasswordTextField
changes. So let's imagine the following scenario, ignoring (for now) the length requirement:
------------------- initial state -------------------
-------------
password: | | passwordValid: YES
-------------
-------------
confirm: | | confirmValid: YES
-------------
-------------- user types in a password --------------
-------------
password: | asdf | passwordValid: NO
-------------
-------------
confirm: | | confirmValid: YES (this will only recalculate when the confirm field changes, not when the password field changes)
-------------
------- user types in the password confirmation -------
-------------
password: | asdf | passwordValid: NO (this didn't recalculate because the contents of the password field didn't change)
-------------
-------------
confirm: | asdf | confirmValid: YES
-------------
Aha! So we need to recalculate when either of them changes. And, really, that should be a separate signal from the lengths:
RACSignal *passwordsMatch = [RACSignal combineLatest:@[passwordTextField.rac_textSignal,
confirmPasswordTextField.rac_textSignal]
reduce:(NSString *password, NSString *confirm) {
return @([password isEqualToString:confirm]);
}];
RACSignal *isPasswordLongEnough = [passwordTextField.rac_textSignal map:^(NSString *text) {
return @(text.length >= 6);
}];
Then we can put it all together:
RACSignal *signUpActiveSignal = [[RACSignal combineLatest:@[validEmailSignal,
isPasswordLongEnough,
passwordsMatch]] and];
Which is great, because that's sort of how we think of it: "You can sign in when the email address is valid, the password you entered is long enough, and the password confirm field matches the password." Declarative!