Swiftpack.co - Package - nsagora/validation-toolkit

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.

nsagora/validation-toolkit

Lightweight framework for input validation written in Swift

Validation Toolkit badge-version

badge-github badge-codecov badge-docs badge-license badge-twitter

  1. Introduction
  2. Requirements
  3. Installation
  4. Usage Examples
  5. Contribute
  6. Meta

Introduction

ValidationToolkit is designed to be a lightweight framework specialised in 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 ValidationToolkit as to an adjustable wrench more than to a Swiss knife.

With this idea in mind, the toolkit is composed from a small set of protocols and structs that can be easily composed to fit your project needs.

All platforms availability

Since validation can take place at many levels, ValidationToolkit is designed to support iOS, macOS, tvOS, watchOS and native Swift projects, such as server apps.

Open to extensibility

Every project is unique in it's challenges and it's great when we can focus on solving them instead of spending our time on boilerplate tasks.

ValidationToolkit is compact and offers you the foundation you need to build data validation around your project needs. In addition, it includes a set of common validation predicates that most of the projects can benefit of: email validation, required fields, password matching, url validation and many more to come.

Requirements

  • iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.1+
  • Swift 4.2+

Installation

Swift Package Manager

You can use the Swift Package Manager to install ValidationToolkit by adding it to your Package.swift file:

import PackageDescription

let package = Package(
    name: "YOUR_PROJECT_NAME",
    targets: [],
    dependencies: [
        .Package(url: "https://github.com/nsagora/validation-toolkit", majorVersion: 1),
    ]
)

Usage example

For a comprehensive list of examples try the Examples.playground:

  1. Download the repository locally on your machine
  2. Open the project in Xcode
  3. Select the Examples playgrounds from the Project navigator

Predicates

The Predicate represents the core protocol and has the role to evaluate if an input matches on a given validation condition.

At ValidationToolkit's core we have the following two predicates, which allow developers to compose predicates specific to the project needs.

RegexPredicate
let predicate = RegexPredicate(expression: "^[a-z]$")
predicate.evaluate(with: "a") // returns true
predicate.evaluate(with: "5") // returns false
predicate.evaluate(with: "ab") // returns false
BlockPredicate
let pred = BlockPredicate<String> { $0.characters.count > 2 }
predicate.evaluate(with: "a") // returns false
predicate.evaluate(with: "abc") // returns true

In addition, the toolkit offers a set of common validation predicates that your project can benefit of:

EmailPredicate
let predicate = EmailPredicate()
predicate.evaluate(with: "hello@") // returns false
predicate.evaluate(with: "hello@nsagora.com") // returns true
predicate.evaluate(with: "héllo@nsagora.com") // returns true
URLPredicate
let predicate = URLPredicate()
predicate.evaluate(with: "http://www.url.com") // returns true
predicate.evaluate(with: "http:\\www.url.com") // returns false
PairMatchingPredicate
let predicate = PairMatchingPredicate()
predicate.evaluate(with: ("swift", "swift")) // returns true
predicate.evaluate(with: ("swift", "obj-c")) // returns false

On top of that, developers can build more advanced or complex predicates by extending the Predicate protocol, and/ or by composing or decorating the existing predicates:

Custom Predicate
public class MinLenghtPredicate: Predicate {

    public typealias InputType = String

    private let minLenght:Int

    public init(minLenght:Int) {
        self.minLenght = minLenght
    }

    public func evaluate(with input: String) -> Bool {
        return input.characters.count >= minLenght
    }
}

let predicate = MinLenghtPredicate(minLenght: 5)
predicate.evaluate(with: "alph") // returns false
predicate.evaluate(with: "alpha") // returns true
predicate.evaluate(with: "alphabet") // returns true

Constraints

Predicate Constraint

A PredicateConstraint represents a data type that links a Predicate to an Error, in order to provide useful feedback for the end users.

PredicateConstraint
let predicate = BlockPredicate<String> { $0 == "Mr. Goodbytes" }
let constraint = PredicateConstraint(predicate: predicate, error: MyError.magicWord)

let result = constraint.evaluate(with: "please")
switch result {
case .valid:
    print("access granted...")
case .invalid(let summary):
    print("Ah Ah Ah! You didn't say the magic word!")
}  // prints "Ah Ah Ah! You didn't say the magic word!"
enum MyError: Error {
    case magicWord
}

Compound Contraint

A CompoundContraint represents a composition of constraints that allows the evaluation to be made on:

  • any of the constraints
  • all constraints

To provide context, a CompoundContraint allows us to constraint a piece of data as being required and also as being a valid email.

ConstraintSetAn example of a registration form, whereby users are prompted to enter a strong password. This process typically entails some form of validation, but the logic itself is often unstructured and spread out through a view controller.

ValidationToolkit seeks instead to consolidate, standardise, and make explicit the logic that is being used to validate user input. To this end, the below example demonstrates construction of a full CompoundContraint object that can be used to enforce requirements on the user's password data:

let lowerCasePredicate = RegexPredicate(expression: "^(?=.*[a-z]).*$")
let upperCasePredicate = RegexPredicate(expression: "^(?=.*[A-Z]).*$")
let digitsPredicate = RegexPredicate(expression: "^(?=.*[0-9]).*$")
let specialChars = RegexPredicate(expression: "^(?=.*[!@#\\$%\\^&\\*]).*$")
let minLenght = RegexPredicate(expression: "^.{8,}$")

var passwordConstraint = CompoundContraint<String>(allOf:
    PredicateConstraint(predicate: lowerCasePredicate, error: Form.Password.missingLowercase),
    PredicateConstraint(predicate: upperCasePredicate, error: Form.Password.missingUpercase),
    PredicateConstraint(predicate: digitsPredicate, error: Form.Password.missingDigits),
    PredicateConstraint(predicate: specialChars, error: Form.Password.missingSpecialChars),
    PredicateConstraint(predicate: minLenght, error: Form.Password.minLenght(8))
)

let password = "3nGuard!"
let result = passwordConstraint.evaluate(with: password)

switch result {
case .success:
    print("Wow, that's a 💪 password!")
case .failure(let summary):
    print(summary.errors.map({$0.localizedDescription}))
} // prints "Wow, that's a 💪 password!"

From above, we see that once we've constructed the passwordConstraint, we're simply calling evaluate(with:) to get our evaluation Result. This contains Summary that can be handled as we please.

Contribute

We would love you for the contribution to ValidationToolkit, check the LICENSE file for more info.

Meta

This project is developed and maintained by the members of iOS NSAgora, the community of iOS Developers of Iași, Romania.

Distributed under the MIT license. See LICENSE for more information.

[https://github.com/nsagora/validation-toolkit]

Github

link
Stars: 31
Last commit: 4 hours ago

Releases

Release v0.7.0 - 2021-02-19T15:50:11

  • Introduce the CompoundConstraint and TypeConstraint
  • Remove ContraintsSet
  • Remove the async constraints