Developing iOS Mobile Applications: How to Create a Universal Input Field with the Validation Rules
February 2nd, 2017

When you work with developing text fields in iOS, it is always nice when there’s a way to optimize that process and make it take less time. Examples, where this iOS input field validation is applicable are registration/authorization check to see whether the data entered into the fields was correct. Today we’re going to talk how to do it.
UITextField object is a controller that displays edited text input field and sends an action message to the goal object when the user hits Return (Enter) button. Usually, you use this class in order to get a text information from the user and perform certain actions (like searching) based on this text.
We often use UITextField graphic components in our projects and we should perform a field verification/validation, depending on its requirements.
Main goals of universal field creation:
- Increasing supportability speed and functionality extensibility
- Accelerating developer’s work
- Reducing the lines of code in the class required for validation fields
- Allowing to set parameters in Interface Builder, which, in turn, will accelerate the development as well.
Validation classes are certain rules for text input box check-up:
- SKBaseTextValidator gives an opportunity to check the text on the basis of the specified minimum and maximum length of the text
- SKEmailValidator checks the text for the required e-mail format
- SKPasswordValidator provides an opportunity to check whether the password corresponds to the requested format.
Let’s take a closer look at validation classes’ parameters.
SKBaseTextValidator
SKBaseTextValidator has two parameters: minTextLength and maxTextLength. These two parameters define the minimum and maximum text length.
From the list, you can see that minTextLength by default is set to 0 and maxTextLength is set to default 256 symbols. IB_DESIGNABLE attribute makes Xcode render the class into the Interface Builder, and IBInspectable parameter provides an opportunity to set a value for Interface Builder.
Therefore, if you need to increase minTextLength or decrease maxTextLength, you can do it in the Interface Builder as well as in the code itself.
Base class for validation:
#import <Foundation/Foundation.h>
IB_DESIGNABLE
@interface SKBaseTextValidator : NSObject
@property (assign, nonatomic) IBInspectable NSInteger minTextLength; // Default is 0
@property (assign, nonatomic) IBInspectable NSInteger maxTextLength; // Default is 256
@property (assign, nonatomic) IBInspectable BOOL escapeWhitespaces; // Default YES
- (BOOL)isTextValid:(NSString *)text;
@end
SKEmailValidator
SKEmailValidator has such parameters as mailRegularExpresionString and emailPredicateString.
The first parameter is a regular expression that’s responsible for the e-mail format, and the second one is a predicate for the check-up.
These parameters also a part of the IBInspectable and you can set their values in the Interface Builder.
E-mail validation class:
#import "SKBaseTextValidator.h"
IB_DESIGNABLE
@interface SKEmailValidator : SKBaseTextValidator
@property (strong, nonatomic) IBInspectable NSString *emailRegularExpresionString; // Default @"[A-Z0-9a-z\\._%+-]+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}"
@property (strong, nonatomic) IBInspectable NSString *emailPredicateString; // Default @"SELF MATCHES %@"
@end
SKPasswordValidator
SKPasswordValidator has parameters passwordRegularExpresionString and passwordPredicateString.
The first parameter is a format for password expression, and the second one is the class check predicate. Just like with the SKEmailValidator’s parameters, these parameters are a part of the IBInspectable and their values can be defined in the Interface Builder.
Password validation class:
#import "SKBaseTextValidator.h"
@interface SKPasswordValidator : SKBaseTextValidator
@property (strong, nonatomic) IBInspectable NSString *passwordRegularExpresionString;// Default @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$"
@property (strong, nonatomic) IBInspectable NSString *passwordPredicateString; // Default @"SELF MATCHES %@"
@end
When you need to create your own validation rule, you can inherit the base class, enter the necessary values and rewrite isTextValid method.
Let’s take a look at SKBaseTextField. This class is inherited from UITextField and expands its abilities. As in the files of validation rules, the base class has properties of IBInspectable in order for the developer to be able to set their values in Interface Builder.
Properties:
- validationRule - defines when to perform the field validation. Possible values:
- 0 (SKValidationRuleOnEndEditing) - means that text validation is performed when the user is done entering the text.
- 1 (SKValidationRuleOnTextChanged) - means the text validation would be performed while the user is entering the text.
- validationEnabled - turns the field validation on / off.
- textValidator - defines the field validator. It is SKBaseTextValidator by default.
- textDidChange - block, that is called upon when the text field is changed.
- validationResult - block called during validation. It includes the field validation result.
- valid - read-only property that gives the opportunity to find out whether the field is valid or not, without launching the validation again.
Methods:
- validate - launches the validation process and returns the results without using the blocks.
Base class for universal input field:
#import <UIKit/UIKit.h>
#import "Defines.h"
#import "SKBaseTextValidator.h"
#import "SKTextFieldAccessoryView.h"
typedef NS_ENUM(NSInteger, SKValidationRule) {
SKValidationRuleOnEndEditing,
SKValidationRuleOnTextChanged
};
typedef NS_ENUM(NSInteger, SKValidationRsult) {
SKValidationResultFailed,
SKValidationResultSuccess
};
IB_DESIGNABLE
@interface SKBaseTextField : UITextField
//properties
@property (assign, nonatomic) IBInspectable BOOL validationEnabled; // Default is YES
@property (assign, nonatomic) IBInspectable NSInteger validationRule; // SKValidationRule 0 or 1
//validator
@property (strong, nonatomic) IBOutlet SKBaseTextValidator *textValidator; // Validator
//view
@property (weak, nonatomic) IBOutlet SKTextFieldAccessoryView *accessoryView; // View that can be changed according to validation results or else
//readonly properties
@property (readonly, assign, getter=isValid) BOOL valid;
//callbacks
@property (copy, nonatomic) void (^textDidChange) (SKBaseTextField *textField, NSString *text);
@property (copy, nonatomic) void (^validationResult)(SKBaseTextField *textField, NSString *text, SKValidationRsult result);
- (BOOL)validate;
@end
In order to use this field, declare it as a property:
@property (weak, nonatomic) IBOutlet SKEmailTextField *emailTextFieldUnderline;
If necessary, set the validation result block and text change block. Depending on the validation results, the developer can describe the behavior of the field and/or other components’, that depend on the input result.
self.emailTextFieldUnderline.textDidChange = ^(SKBaseTextField *textField, NSString *text) {
NSLog(@"%@",text);
};
self.emailTextFieldUnderline.validationResult = ^(SKBaseTextField *textField, NSString *text, SKValidationRsult result){
switch (result) {
case SKValidationResultFailed: {
NSLog(@"validation failed - %@",text);
break;
}
case SKValidationResultSuccess: {
NSLog(@"validation success - %@",text);
break;
}
}
};
In order to set a field validation rule in the Interface Builder, we need to add an NSObject and set a class necessary for it. You can set the field validator via Interface Builder.
- Set validator class in the Interface Builder
- Set validator’s parameters in the Interface Builder
- Set field parameters in the Interface Builder
Examples of usage
Using SKEmailtTextField + SKEmailValidator
The field detects whether the email is valid or not while the user is typing it in and highlights it accordingly depending on the result.
The code itself:
@property (weak, nonatomic) IBOutlet SKEmailTextField *emailTextField;
@property (copy, nonatomic) void (^lineDrawingBlock)(SKTextFieldAccessoryView *view); // line
self.emailTextField.validationResult = self.validationResult;
self.emailTextField.accessoryView.drawingBlock = self.lineDrawingBlock;
- (void (^)(SKTextFieldAccessoryView *))lineDrawingBlock {
if (!_lineDrawingBlock) {
_lineDrawingBlock = ^(SKTextFieldAccessoryView *view) {
[[view currentStateColor] setFill];
UIRectFill(view.bounds);
};
}
return _lineDrawingBlock;
}
- (void (^)(SKBaseTextField *, NSString *, SKValidationRsult))validationResult {
if (!_validationResult) {
_validationResult = ^(SKBaseTextField *textField, NSString *text, SKValidationRsult result){
switch (result) {
case SKValidationResultFailed: {
NSLog(@"validation failed - %@",text);
[textField.accessoryView setCurrentState:SKAccessoryViewStateError];
break;
}
case SKValidationResultSuccess: {
NSLog(@"validation success - %@",text);
[textField.accessoryView setCurrentState:SKAccessoryViewStateSuccess];
break;
}
}
};
}
return _validationResult;
}
Using SKPasswordTextField + SKPasswordValidator
The field detects whether the password is correct or not after the user is done typing and highlight the result according to the validation result.
How does the code look?
@property (weak, nonatomic) IBOutlet SKPasswordTextField *passwordTextField;
@property (copy, nonatomic) void (^borderDrawingBlock)(SKTextFieldAccessoryView *view); // border
self.passwordTextField.validationResult = self.validationResult;
self.passwordTextField.accessoryView.drawingBlock = self.borderDrawingBlock;
- (void (^)(SKTextFieldAccessoryView *))borderDrawingBlock {
if (!_borderDrawingBlock) {
_borderDrawingBlock = ^(SKTextFieldAccessoryView *view) {
[view makeRoundedCorners:5.];
[view makeBorder:1. color:[view currentStateColor]];
};
}
return _borderDrawingBlock;
}
- (void (^)(SKBaseTextField *, NSString *, SKValidationRsult))validationResult {
if (!_validationResult) {
_validationResult = ^(SKBaseTextField *textField, NSString *text, SKValidationRsult result){
switch (result) {
case SKValidationResultFailed: {
NSLog(@"validation failed - %@",text);
[textField.accessoryView setCurrentState:SKAccessoryViewStateError];
break;
}
case SKValidationResultSuccess: {
NSLog(@"validation success - %@",text);
[textField.accessoryView setCurrentState:SKAccessoryViewStateSuccess];
break;
}
}
};
}
return _validationResult;
}
Using SKDateTExtField + SKBaseValidator
After the date is chosen and OK is hit, the field detects the availability of the chosen date and highlights it according to the result.
The mighty code for that:
@property (weak, nonatomic) IBOutlet SKDatePickerTextField *dateTextField;
@property (copy, nonatomic) void (^lineDrawingBlock)(SKTextFieldAccessoryView *view); // line
self.dateTextField.accessoryView.drawingBlock = self.lineDrawingBlock;
- (void (^)(SKTextFieldAccessoryView *))lineDrawingBlock {
if (!_lineDrawingBlock) {
_lineDrawingBlock = ^(SKTextFieldAccessoryView *view) {
[[view currentStateColor] setFill];
UIRectFill(view.bounds);
};
}
return _lineDrawingBlock;
}
#pragma mark - IBAction
- (IBAction)okButtonPressed:(id)sender {
if ([self.dateTextField validationEnabled]) {
[self.dateTextField.accessoryView setCurrentState:self.dateTextField.isValid];
}
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidBeginEditing:(UITextField *)textField {
[[((SKBaseTextField *)textField) accessoryView] setCurrentState:SKAccessoryViewStateActive];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
Need native iOS or Android development or a cross-platform mobile app?
