A lightweight Swift library for validating and normalizing user input, for example in forms.
Documentation for ValidationKit can be found on GitHub Pages. Documentation is written using DocC, with a documentation catalog included inside the Swift package.
import ValidationKit
// Validators can be combined using the operators `&&` and `||`
let tweetValidator: Validator = .notEmpty && .maxLength(280)
let tweet = "Hello, world!"
let result = tweetValidator.validate(input: tweet)
switch result {
case .valid(let value):
print("Tweet '\(value)' is valid")
case .invalid(let error):
print(String(format: "Tweet did not validate: %@", error.localizedDescription))
}
The built-in validators are:
isEmpty
: String must be emptynotEmpty
: String must not be emptyminLength(_ length: Int)
: String length must be greater than or equal...maxLength(_ length: Int)
: String length must be less than or equal...exactLength(_ length: Int)
: String length must be exactly...isDate(formatter: DateFormatter)
: String must be parsable as a date using the given formatterisInThePast
: Date must be in the pastanyOf
: Value must be one of certain given optionsaccepted
: Value must be boolean truehasPrefix
: String must have a certain prefix
A custom validator can be implemented as follows. The example uses a regular expression, but of course you are free to use any kind of logic you want.
public extension Validator {
static var isDutchPostalCode: Validator<String, String> {
Validator<String, String> { input in
if let range = input.range(of: #"^\d{4}\s?[a-zA-Z]{2}$"#, options: .regularExpression) {
let output = String(input[range])
return .valid(output)
} else {
return .invalid(.invalidDutchPostalCode)
}
}
}
}
public extension ValidationError {
static let invalidDutchPostalCode = ValidationError(localizedDescription: NSLocalizedString("Invalid Dutch postal code", comment: "Validation error text"))
}
We can then use it like so:
let postalCodeValidator: Validator = .notEmpty && .isDutchPostalCode
let result = postalCodeValidator.validate(input: "2516 AH") // Equals ValidationResult.valid("2516 AH")
The input of a validator does not have to be of the same type as its output. For example, this validator converts a string into an array of digits (integers from 0-9).
extension Validator {
/// Validates whether a string consists of a sequence of digits (0-9).
static var numericCode: Validator<String, [Int]> {
Validator<String, [Int]> { input in
let numericCode: [Int] = input
.map(String.init) // Convert the string to individual characters first
.compactMap(Int.init)
if numericCode.count == input.count {
return .valid(numericCode)
} else {
return .invalid(.notNumericCode)
}
}
}
}
extension ValidationError {
static let notNumericCode = ValidationError(localizedDescription: NSLocalizedString("Not a numeric code", comment: "Validation error text"))
}
To validate an 8-digit code, we can use it like so:
let validator: Validator = .exactLength(8) && .numericCode
let result = validator.validate(input: "13374242)
result.isValid // True
result.value // [1, 3, 3, 7, 4, 2, 4, 2]
This library contains localized strings for English and Dutch. Pull requests that add support for additional languages are welcome.
If you desire a different error text for a certain validation, you must write your own validator and validation error. See the files Validators.swift and ValidationError.swift for examples how to do this.
- 1.1.0 - 2024-05-24 - Added email validator
- 1.0.1 - 2023-10-12 - Added isEmpty/notEmpty validators for non-optional string types
- 1.0.0 - 2023-05-25 - After more than two years since the public release, and having been used in multiple production projects, it is time for a 1.0 release!
- 0.0.1 - 2021-03-28 - Initial public release
- 0.0.0 - 2019-12-02 - Initial private version for a project at Q42
ValidationKit is written by Mathijs Bernson and Tim van Steenis.
It is available under the MIT license, so feel free to use it in commercial and non-commercial projects.