valeriyvan / validation-toolkit

Lightweight framework for input validation written in Swift

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Validation Toolkit · Validation Components


Validation Toolkit

travis-status codecov-status carthage-compatible license twitter

Introduction

Validation Toolkit is designed to be a lightweight framework specialised in user data validation, such as email format, input length or passwords matching.

At the core of this project are the following principles:

  • Separation of concerns
  • Availability on all platforms
  • Open to extensibility

Separation of concerns

Think of Validation Toolkit as to an adjustable wrench more than to a Swiss knife. With this idea in mind, the kit is composed from a small set of protocols, classes and structs than ca be easily used to fit your project needs.

All platforms availability

Since validation can take place at many levels, Validation Toolkit was designed to support iOS, macOS, tvOS, watchOS and native swift projects, such as server apps.

Open to extensibility

Every project is uniq in it's challenges and it's great when we can focus on solving them instead on focusing on boiler plate tasks. While Validation Toolkit is compact and offers all you need to build validation around your project needs, we created Validation Components to extends it by adding the common validations that most of the projects can benefits of. This includes validation predicates for email, required fields, password matching, url and many other.

Installation

Carthage

The full documentation on how to install and use Carthage is available on their official github page.

To use the latest stable version, add this to your Cartfile:

github "nsagora/validation-toolkit" 

To use a certain version, add this to your Cartfile:

github "nsagora/validation-toolkit" >= 0.1.0 

To use the latest version in development, add this to your Cartfile:

github "nsagora/validation-toolkit" "develop" 

Concepts

ValidationPredicate

The ValidationPredicate represents the core protocol and has the role to evaluate if an input matches on a given predicate.

Out of the box, ValidationToolkit brings the following predicates, which allow the developers to construct their specific predicates:

  • RegexValidationPredicate
  • BlockValidationPredicate

For more advanced or complex predicates the developers can extend the ValidationPredicate to fit the project needs.

ValidationConstraint

A ValidationConstraint represents a structure that link a ValidationPredicate with a custom message, useful for user feedback.

Validator

A Validator represents a collection of constraints and allows the evaluation to be made on:

  • any of the constraints
  • all constraints

To provide context, a Validator allows to constrain an input as being required and as being a valid email.

Examples

Full Validator Example

The classic validation example is that of the login form, whereby users are prompted to enter their username and password. This process typically entails some form of validation, but the logic itself is often unstructured and spread out through a view controller. Similarly, the logic is often invoked through various user interactions (e.g. typing characters into a field, and tapping a Login button).

Validation Toolkit seeks instead to consolidate, standardize, and make explicit the logic that is being used to validate user input. To this end, the below example demonstrates construction of a full Validator object that can be used to enforce requirements on the username input data:

// Setup a constraint used to check the length of the username
let usernameLengthConstraint = { () -> ValidationConstraint<String> in
    let pred = BlockValidationPredicate<String> {
        ($0 ?? "").characters.count > 2
    }
    let msg = "Username must be at least 3 characters long."
    return ValidationConstraint(predicate: pred, message: msg)
}()

// Setup a constraint used to check the characters contained within the username
let usernameCharactersConstraint = { () -> ValidationConstraint<String> in 
    let pred = RegexValidationPredicate(expression: "^[0-9a-Z_\\-]*?$")
    let msg = "Username must only contain alphanumeric characters, dashes, and underscores."
    return ValidationConstraint<String>(predicate: pred, message: msg)
}()

// Create our validator to compose the two constraints
let usernameConstraints = [usernameLengthConstraint, usernameCharactersConstraint];
let usernameValidator = Validator<String>(constraints: usernameConstraints)

// Evaluate user input, and collect results
let results = usernameValidator.evaluateAll(input: "nsagora")
let errorMessages = results.flatMap { result -> String? in
    switch result {
    case .valid:
        return nil
    case .invalid(let error):
        return error.localizedDescription
    }
}
print(errorMessages)

From above, we see that once we've constructed the usernameValidator, we're simply calling evaluateAll(input:) to get a list of results. These results we can then map into an array of error messages, which we can handle as we please. You can imagine that we might construct this usernameValidator once, and simply evaluate it against the user input data when we want to perform validation on the username.

Independent Components

Along with the fully integrated scenario depicted above, Validator Kit also supports using each component independently. Specifically, we are able to exercise a ValidationPredicate outside of a ValidationConstraint, and similarly exercise a ValidationConstraint outside of a Validator.

ValidationPredicate

let predicate = RegexValidationPredicate(expression: "^[a-z]$")
predicate.evaluate(with: "a") // returns true
predicate.evaluate(with: "5") // returns false
predicate.evaluate(with: "ab") // returns false

ValidationConstraint

let predicate = BlockValidationPredicate<String> { $0 == "Mr. Goodbytes" }
let message = "Ah Ah Ah! You didn't say the magic word!"
let constraint = ValidationConstraint(predicate: predicate, message: message)

let result = constraint.evaluate(with: "please")
switch result {
    case .valid:
        print("access granted...")
    case .invalid(let error):
        print(error.localizedDescription) // prints the message
}

Credits and references

The project was been inspired from other open source projects and they worth to be mentioned below for reference:

About

Lightweight framework for input validation written in Swift

License:Apache License 2.0


Languages

Language:Swift 90.7%Language:Ruby 6.9%Language:Objective-C 2.3%