Declarative data validation framework, written in Swift

Overview

Peppermint badge-version

badge-build-macos badge-build-linux badge-codecov badge-docs badge-license badge-twitter

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

Introduction

let constraint = TypeConstraint<Account, Account.Error> {
    KeyPathConstraint(\.username) {
        BlockConstraint {
            $0.count >= 5
        } errorBuilder: {
            .username
        }
    }
    KeyPathConstraint(\.password) {
        GroupConstraint(.all) {
            PredicateConstraint {
                .characterSet(.lowercaseLetters, mode: .inclusive)
            } errorBuilder: {
                .password(.missingLowercase)
            }
            PredicateConstraint{
                .characterSet(.uppercaseLetters, mode: .inclusive)
            } errorBuilder: {
                .password(.missingUppercase)
            }
            PredicateConstraint {
                .characterSet(.decimalDigits, mode: .inclusive)
            } errorBuilder: {
                .password(.missingDigits)
            }
            PredicateConstraint {
                .characterSet(CharacterSet(charactersIn: "!?@#$%^&*()|\\/<>,.~`_+-="), mode: .inclusive)
            } errorBuilder: {
                .password(.missingSpecialChars)
            }
            PredicateConstraint {
                .length(min: 8)
            }  errorBuilder: {
                .password(.tooShort)
            }
        }
    }
    BlockConstraint {
        $0.password == $0.passwordConfirmation
    } errorBuilder: {
        .password(.confirmationMismatch)
    }
    KeyPathConstraint(\.email) {
        PredicateConstraint(.email, error: .email)
    }
    KeyPathConstraint(\.age) {
        PredicateConstraint(.range(min: 14), error: .underAge)
    }
    KeyPathConstraint(\.website) {
        PredicateConstraint(.url, error: .website)
            .optional()
    }
}

let result = constraint.evaluate(with: account)
switch result {
case .success:
    handleSuccess()
case .failure(let summary):
    handleErrors(summary.errors)
}

Peppermint is a declarative and lightweight data validation framework.

At the core of it, there are 2 principles:

  • Empower composition.
  • Embrace standard library.

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

With this idea in mind, the framework follows the Protocol Oriented Programming paradigm and was designed from a small set of protocols and structures that can easily be composed to fit your project needs. Thus, you can think of Peppermint as an adjustable wrench more than a Swiss knife.

Since validation can take place at many levels, Peppermint is available on iOS, macOS, tvOS, watchOS and native Swift projects, such as server-side apps.

Requirements

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

Installation

Peppermint is available only through Swift Package Manager.

Swift Package Manager

You can add Peppermint to your project in Xcode by going to File > Swift Packages > Add Package Dependency.

Or, if you want to use it as a dependency to your own package, you can add it to your Package.swift file:

import PackageDescription

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

Usage example

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

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

The Peppermint framework 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 and constraints that most projects can benefit off.

Predicates

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

At the core of Peppermint there are the following two predicates, which allows you to compose predicates specific to the project needs:

BlockPredicate
let predicate = BlockPredicate<String> { $0.characters.count > 2 }
predicate.evaluate(with: "a") // returns false
predicate.evaluate(with: "abc") // returns true
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

In addition, the framework 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: "[email protected]") // returns true
predicate.evaluate(with: "[email protected]") // returns true
URLPredicate
let predicate = URLPredicate()
predicate.evaluate(with: "http://www.url.com") // returns true
predicate.evaluate(with: "http:\\www.url.com") // returns false
RangePredicate
let predicate = let range = RangePredicate(10...20)
predicate.evaluate(with: 15) // returns true
predicate.evaluate(with: 21) // returns false
LengthPredicate
let predicate = LengthPredicate<String>(min: 5)
predicate.evaluate(with: "abcde")   // returns true
predicate.evaluate(with: "abcd")    // 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 struct CustomPredicate: Predicate {

    public typealias InputType = String

    private let custom: String

    public init(custom: String) {
        self.custom = custom
    }

    public func evaluate(with input: String) -> Bool {
        return input == custom
    }
}

let predicate = CustomPredicate(custom: "alphabet")
predicate.evaluate(with: "alp") // returns false
predicate.evaluate(with: "alpha") // returns false
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 constraint = PredicateConstraint<String, MyError>(.email, error: .invalid)

let result = constraint.evaluate(with: "[email protected]")
switch result {
case .valid:
    print("Hi there 👋!")
case .invalid(let summary):
    print("Oh, I was expecting a valid email address!")
}  // prints "Hi there 👋!"
enum MyError: Error {
    case invalid
}

Block Constraint

A BlockConstraint represents a data type that links a custom validation closure to an Error that describes why the evaluation has failed. It's a shortcut of a PredicateConstraint that is initialised with a BlockPredicate.

BlockConstraint
let constraint = BlockConstraint<Int, MyError> {
    $0 % 2 == 0
} errorBuilder: {
    .magicNumber
}

constraint.evaluate(with: 3)
enum Failure: MyError {
    case magicNumber
}

Group Constraint

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

  • all constraints
  • or any of the constraints

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

GroupConstraintAn 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.

Peppermint 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 GroupConstraint object that can be used to enforce requirements on the user's password data:

var passwordConstraint = GroupConstraint<String, Form.Password>(.all) {
    PredicateConstraint {
        .characterSet(.lowercaseLetters, mode: .loose)
    } errorBuilder: {
        .missingLowercase
    }
    PredicateConstraint{
        .characterSet(.uppercaseLetters, mode: .loose)
    } errorBuilder: {
        .missingUppercase
    }
    PredicateConstraint {
        .characterSet(.decimalDigits, mode: .loose)
    } errorBuilder: {
        .missingDigits
    }
    PredicateConstraint {
        .characterSet(CharacterSet(charactersIn: "!?@#$%^&*()|\\/<>,.~`_+-="), mode: .loose)
    } errorBuilder: {
        .missingSpecialChars
    }
    PredicateConstraint {
        .length(min: 8)
    }  errorBuilder: {
        .minLength(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 a Summary that can be handled as we please.

Contribute

We would love you for the contribution to Peppermint, 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/peppermint]

You might also like...
Former is a fully customizable Swift library for easy creating UITableView based form.
Former is a fully customizable Swift library for easy creating UITableView based form.

Former is a fully customizable Swift library for easy creating UITableView based form. Submitting Issues Click HERE to get started with filing a bug r

XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift & Obj-C.
XLForm is the most flexible and powerful iOS library to create dynamic table-view forms. Fully compatible with Swift & Obj-C.

XLForm By XMARTLABS. If you are working in Swift then you should have a look at Eureka, a complete re-design of XLForm in Swift and with more features

Custom-TopBarController - A Custom TopBar Controller With Swift
Custom-TopBarController - A Custom TopBar Controller With Swift

TopBarController Верстка Для IPhone и IPod вертска адаптивная, для IPad frane To

GrouponHeader - iOS TableView Header Animation, Swift/UIKit
GrouponHeader - iOS TableView Header Animation, Swift/UIKit

GrouponHeader Description: iOS TableView Header Animation Technology: Swift, UIK

AtomicReferenceCell - Atomic Reference Cell (Arc) for Swift

Atomic Reference Cell This project provide two structures: ArcT and WeakArcT

Declarative data validation framework, written in Swift

Peppermint Introduction Requirements Installation Swift Package Manager Usage Examples Predicates Constraints Predicate Constraint Compound Constraint

iOS validation framework with form validation support

ATGValidator ATGValidator is a validation framework written to address most common issues faced while verifying user input data. You can use it to val

iOS validation framework with form validation support

ATGValidator ATGValidator is a validation framework written to address most common issues faced while verifying user input data. You can use it to val

SwiftCop is a validation library fully written in Swift and inspired by the clarity of Ruby On Rails Active Record validations.
SwiftCop is a validation library fully written in Swift and inspired by the clarity of Ruby On Rails Active Record validations.

SwiftCop is a validation library fully written in Swift and inspired by the clarity of Ruby On Rails Active Record validations. Objective Build a stan

Monarch Router is a Declarative URL- and state-based router written in Swift.
Monarch Router is a Declarative URL- and state-based router written in Swift.

Monarch Router is a declarative routing handler that is capable of managing complex View Controllers hierarchy transitions automatically, decoupling View Controllers from each other via Coordinator and Presenters. It fits right in with Redux style state flow and reactive frameworks.

A simple, declarative, functional drawing framework, in Swift!
A simple, declarative, functional drawing framework, in Swift!

DePict - A simple, declarative, functional drawing framework. To produce a drawing, call the Draw function (just type Draw and let autocomplete do the

Declarative Swift framework for Attributed Role-based Access Control management
Declarative Swift framework for Attributed Role-based Access Control management

Koosa Declarative Swift framework for Attributed Role-based Access Control management Check out this blog post for full explanation and more details:

A simple, declarative, functional drawing framework, in Swift!
A simple, declarative, functional drawing framework, in Swift!

DePict - A simple, declarative, functional drawing framework. To produce a drawing, call the Draw function (just type Draw and let autocomplete do the

Lightweight declarative auto-layout framework for Swift

SwiftyLayout SwiftyLayout is a framework that allows to describe layout constraints (ie NSLayoutConstraint) as a simple mathematical formula in a Swif

Swift Validator is a rule-based validation library for Swift.
Swift Validator is a rule-based validation library for Swift.

Swift Validator is a rule-based validation library for Swift. Core Concepts UITextField + [Rule] + (and optional error UILabel) go into

Meet Corvus, the first strongly declarative server-side framework.
Meet Corvus, the first strongly declarative server-side framework.

Corvus Corvus is the first truly declarative server-side framework for Swift. It provides a declarative, composable syntax which makes it easy to get

Meet Corvus, the first strongly declarative server-side framework.
Meet Corvus, the first strongly declarative server-side framework.

Corvus Corvus is the first truly declarative server-side framework for Swift. It provides a declarative, composable syntax which makes it easy to get

Declarative iOS UI sugar framework built on FlexLayout
Declarative iOS UI sugar framework built on FlexLayout

Declarative iOS UI sugar framework built on FlexLayout

A declarative UI framework for iOS
A declarative UI framework for iOS

Layout Layout is a native Swift framework for implementing iOS user interfaces using XML template files and runtime-evaluated expressions. It is inten

Comments
  • Bump excon from 0.70.0 to 0.71.1

    Bump excon from 0.70.0 to 0.71.1

    Bumps excon from 0.70.0 to 0.71.1.

    Changelog

    Sourced from excon's changelog.

    0.71.1 2019-12-18

    fix frozen chunks through dup prior to binary_encode

    0.71.0 2019-12-12

    fix for leftover data with interrupted persistent connections

    Commits
    • beb02b4 v0.71.1
    • ac85f68 Merge pull request #711 from unasuke/frozen_string_literal
    • 6609703 Use String#dup in Excon::Utils#binary_encode for frozen string
    • d498014 Add ruby-head to travis build matrix as allow_failure
    • 1149d44 v0.71.0
    • ccb57d7 fix for leftover data with interrupted persistent connections
    • See full diff in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot ignore this [patch|minor|major] version will close this PR and stop Dependabot creating any more for this minor/major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • Introduce the concept of modifier

    Introduce the concept of modifier

    Inspired from SwiftUI,a modifier is a method on a constraint that returns a new constraint by decorating the caller with more functionality.

    For example, one may call the .optional() modifier to return an OptionalConstraint.

    The purpose is to simplify the API syntax.

    enhancement 
    opened by alexcristea 0
  • Add support for DocC

    Add support for DocC

    Apple introduced DocC at WWDC21. It is a documentation compiler which allows framework and package authors to write and publish reach and interactive documentation for their clients of their software.

    enhancement 
    opened by alexcristea 0
  • Define a MR template and checklist

    Define a MR template and checklist

    Create an MR template that contains a checklist of activities to perform before submitting the MR. This checklist should include like tests passing, every target builds, pod lint is successful, etc.

    enhancement help wanted 
    opened by alexcristea 0
Releases(1.2.0)
Owner
iOS NSAgora
iOS NSAgora
Declarative form building framework for iOS

Formalist Swift framework for building forms on iOS Formalist is a Swift framework for building forms on iOS using a simple, declarative, and readable

Seed 159 May 25, 2022
A rule-based validation library for Swift

SwiftValidator Swift Validator is a rule-based validation library for Swift. Core Concepts UITextField + [Rule] + (and optional error UILabel) go into

null 1.4k Dec 29, 2022
Custom Field component with validation for creating easier form-like UI from interface builder.

#YALField Custom Field component with validation for creating easier form-like UI from interface builder. ##Example Project To run the example project

Yalantis 476 Sep 1, 2022
iOS Validation Library

Honour Validation library for iOS inspired by Respect/Validation. Validator.mustBe(Uppercase()).andMust(StartsWith("F")).validate("FOOBAR") ❗ If you w

Jean Pimentel 55 Jun 3, 2021
APValidators - Codeless solution for form validation in iOS!

APValidators is a codeless solution for form validation. Just connect everything right in Interface Builder and you're done. Supports really complex and extendable forms by allowing to connect validators in tree.

Alty 131 Aug 16, 2022
Carbon🚴 A declarative library for building component-based user interfaces in UITableView and UICollectionView.

A declarative library for building component-based user interfaces in UITableView and UICollectionView. Declarative Component-Based Non-Destructive Pr

Ryo Aoyama 1.2k Jan 5, 2023
SwiftForms is a small and lightweight library written in Swift that allows you to easily create forms.

SwiftForms is a powerful and extremely flexible library written in Swift that allows to create forms by just defining them in a couple of lines. It also provides the ability to customize cells appearance, use custom cells and define your own selector controllers.

Miguel Ángel Ortuño 1.3k Dec 27, 2022
SwiftyFORM is a lightweight iOS framework for creating forms

SwiftyFORM is a lightweight iOS framework for creating forms Because form code is hard to write, hard to read, hard to reason about. Has a

Simon Strandgaard 1.1k Dec 29, 2022
A framework to validate inputs of text fields and text views in a convenient way.

FormValidatorSwift The FormValidatorSwift framework allows you to validate inputs of text fields and text views in a convenient way. It has been devel

ustwo™ 500 Nov 29, 2022
Elegant iOS form builder in Swift

Made with ❤️ by XMARTLABS. This is the re-creation of XLForm in Swift. 简体中文 Overview Contents Requirements Usage How to create a Form Getting row valu

xmartlabs 11.6k Jan 1, 2023