ReactiveCocoa and form validation using throttle

I’m a big fan of using ReactiveCocoa in the apps I build. I’m using it through out the whole application, from Networking to UI related stuff. One thing ReactiveCocoa especially helps solving easily is form validation.

ReactiveCocoa contains many methods and some are more familiar than others. One handy method is using the throttle with valuesPassingTest, where you can wait for X seconds to continue handling the signal.
Imagine a user entering his email. You want to implement this scenario:

  1. User starts typing
  2. User gets feedback about the input validation
  3. The form shows some UI response if the input is invalid

Point 3 may only appear when the user says:

Hé, I’m done, tell me if my email is correct

Instead of:

Hé, I’m still typing, why are you saying my email is wrong, I know that!

ReactiveCocoa’s throttle

So there we are, time for some nice code!

First we have the email validation signal:

var validEmailSignal = emailTextField.rac_newTextChannel().map { (text:AnyObject!) -> AnyObject! in
    return (text as? String)?.isValidEmail() ?? false
}

Which we assign to the enabled property of the register button:

// Assign signal to register button
RAC(registerButton!, "enabled") <~ validEmailSignal

Then we create the color feedback for user feedback about the input:

// Create validation colors
RAC(usernameContainer, "backgroundColor") <~ textFieldColorValidationWithSignal(validEmailSignal, textField: emailTextField)

Which is using the throttle:valuesPassingTest: method:

private func textFieldColorValidationWithSignal(signal:RACSignal, textField:UITextField) -> RACSignal {
    return signal.filter({ (_) -> Bool in
        // Make sure we have input and we're still the active textField
        return textField.text.length > 0 && textField.isFirstResponder()
    }).throttle(1.5, valuesPassingTest: { (object) -> Bool in
        // Wait for 1.5 seconds before continuing and showing the coloured background feedback
        // If a `next` is received, and then another `next` is received before
        // `interval` seconds have passed, the coloured background won't appear yet
        return (object as? Bool) == false
    }).map({ [unowned self] (object) in
        self.colorForValidity(object)
    })
}

private func colorForValidity(valid : AnyObject!) -> UIColor! {
    return (valid as? Bool) == false ? UIColor.redColor() : UIColor.clearColor()
}

This creates a really nice implementation (documentation here). As long as the user is typing, no form validation feedback will be shown. If the user stops typing for 1.5 seconds, the input gets validated and shows a red background when invalid.

The valuesPassingTest block is used to continue directly. For instance, if a user entered a wrong input earlier and corrects it to valid, the signal will continue without the throttle of 1.5 seconds. It will instantly clear the red background. Nice isn’t it?

 

Antoine van der Lee

Dutch iOS developer at Triple. Developed apps like Buienradar, Videoland and Pop the Dots.

 
Follow on Feedly