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.
For a comprehensive list of examples try out the Examples.playground:
Download the repository locally on your machine
Open the project in Xcode
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:
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: "hello@nsagora.com")
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.
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.
Peppermint
Introduction
Peppermint
is a declarative and lightweight data validation framework.At the core of it, there are 2 principles:
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
Installation
Peppermint
is available only through Swift Package Manager.Swift Package Manager
You can add
Peppermint
to your project in Xcode by going toFile > 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:Usage example
For a comprehensive list of examples try out the
Examples.playground
:Examples
playground from the Project navigatorThe
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 coreprotocol
and has the role toevaluate
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
RegexPredicate
In addition, the framework offers a set of common validation predicates that your project can benefit of:
EmailPredicate
URLPredicate
RangePredicate
LengthPredicate
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
Constraints
Predicate Constraint
A
PredicateConstraint
represents a data type that links aPredicate
to anError
, in order to provide useful feedback for the end users.PredicateConstraint
Block Constraint
A
BlockConstraint
represents a data type that links a custom validation closure to anError
that describes why the evaluation has failed. It’s a shortcut of aPredicateConstraint
that is initialised with aBlockPredicate
.BlockConstraint
Group Constraint
A
GroupConstraint
represents a composition of constraints that allows the evaluation to be made on:To provide context, a
GroupConstraint
allows us to constraint a piece of data as being required and also as being a valid email.GroupConstraint
An 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 fullGroupConstraint
object that can be used to enforce requirements on the user’s password data:From above, we see that once we’ve constructed the
passwordConstraint
, we’re simply callingevaluate(with:)
to get our evaluationResult
. This contains aSummary
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]