Skip to content

Repository files navigation

A powerful, type-safe validation framework for Swift

validator

LicenceSwift CompatibilityPlatform CompatibilityCI

Description

Validator is a modern, lightweight Swift framework that provides elegant and type-safe input validation. Built with Swift's powerful type system, it seamlessly integrates with both UIKit and SwiftUI, making form validation effortless across all Apple platforms.

Features

Type-Safe Validation - Leverages Swift's type system for compile-time safety
🎯 Rich Rule Set - Built-in validators for common use cases
🔧 Extensible - Easy to create custom validation rules
📱 UIKit Integration - First-class support for UITextField and other UIKit components
🎨 SwiftUI Native - Property wrappers and view modifiers for declarative validation
📋 Form Management - Validate multiple fields with centralized state management
Lightweight - Minimal footprint with zero dependencies
🧪 Well Tested - Comprehensive test coverage

Table of Contents

Requirements

PlatformMinimum Version
iOS16.0+
macOS13.0+
tvOS16.0+
watchOS9.0+
visionOS1.0+
Xcode15.3+
Swift5.10+

Usage

The package contains two libraries: ValidatorCore encompasses all validation logic and predefined validators, while ValidatorUI implements extensions for integrating the validator into UI objects. It supports both SwiftUI and UIKit.

Installation

Swift Package Manager

Add the following dependency to your Package.swift:

dependencies:[.package(url:"https://github.com/space-code/validator.git", from:"1.5.0")]

Or add it through Xcode:

  1. File > Add Package Dependencies
  2. Enter package URL: https://github.com/space-code/validator.git
  3. Select version requirements

Quick Start

import ValidatorCore
letvalidator=Validator()letresult= validator.validate(
input:"user@example.com",
rule:RegexValidationRule(
pattern:"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}",
error:"Invalid email address"))switch result {case.valid:print("✅ Valid input")case.invalid(let errors):print("❌ Validation failed: \(errors.map(\.message))")}

Usage

The framework provides two main libraries:

  • ValidatorCore - Core validation logic and predefined validators
  • ValidatorUI - UI integration for UIKit and SwiftUI

Core Validation

Validate any input with the Validator class:

import ValidatorCore
letvalidator=Validator()letresult= validator.validate(
input:"password123",
rule:LengthValidationRule(
min:8,
error:"Password must be at least 8 characters"))

UIKit Integration

Import ValidatorUI to add validation to UIKit components:

import UIKit
import ValidatorUI
import ValidatorCore
classViewController:UIViewController{letemailField=UITextField()overridefunc viewDidLoad(){
super.viewDidLoad()
// Add validation rules
emailField.add(
rule:RegexValidationRule(
pattern:"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}",
error:"Please enter a valid email"))
// Enable real-time validation
emailField.validateOnInputChange(isEnabled:true)
// Handle validation results
emailField.validationHandler ={ result inswitch result {case.valid:self.updateUI(isValid:true)case.invalid(let errors):self.showErrors(errors)}}}}

SwiftUI Integration

Single Field Validation

Use the .validation() modifier for simple field validation:

import SwiftUI
import ValidatorUI
import ValidatorCore
structLoginView:View{@Stateprivatevaremail=""varbody:someView{TextField("Email", text: $email).validation($email, rules:[RegexValidationRule(
pattern:"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}",
error:"Invalid email")]){ result inif case .invalid(let errors)= result {print("Validation errors: \(errors)")}}}}

Or use .validate() with a custom error view:

structLoginView:View{@Stateprivatevarpassword=""varbody:someView{VStack(alignment:.leading){SecureField("Password", text: $password).validate(item: $password, rules:[LengthValidationRule(min:8, error:"Too short")]){ errors inForEach(errors, id: \.message){ error inText(error.message).foregroundColor(.red).font(.caption)}}}}}

Form Validation

Manage multiple fields with FormFieldManager:

import Combine
import SwiftUI
import ValidatorUI
import ValidatorCore
classRegistrationForm:ObservableObject{@Publishedvarmanager=FormFieldManager()@FormField(rules:[LengthValidationRule(min:2, max:50, error:"Invalid name length")])varfirstName=""@FormField(rules:[LengthValidationRule(min:2, max:50, error:"Invalid name length")])varlastName=""@FormField(rules:[RegexValidationRule( pattern:"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}", error:"Invalid email")])varemail=""
lazy varfirstNameContainer= _firstName.validate(manager: manager)
lazy varlastNameContainer= _lastName.validate(manager: manager)
lazy varemailContainer= _email.validate(manager: manager)}structRegistrationView:View{@StateObjectprivatevarform=RegistrationForm()@StateprivatevarisFormValid=falsevarbody:someView{Form{Section("Personal Information"){TextField("First Name", text: $form.firstName).validate(validationContainer: form.firstNameContainer){ errors inErrorView(errors: errors)}TextField("Last Name", text: $form.lastName).validate(validationContainer: form.lastNameContainer){ errors inErrorView(errors: errors)}}Section("Contact"){TextField("Email", text: $form.email).validate(validationContainer: form.emailContainer){ errors inErrorView(errors: errors)}}Section{Button("Submit"){
form.manager.validate()}.disabled(!isFormValid)}}.onReceive(form.manager.$isValid){ newValue in
isFormValid = newValue
}}privatefunc submitForm(){print("✅ Form is valid, submitting...")}}

Built-in Validators

ValidatorDescriptionExample
LengthValidationRuleValidates string length (min/max)LengthValidationRule(min: 3, max: 20, error: "Length must be 3-20 characters")
NonEmptyValidationRuleEnsures string is not empty or blankNonEmptyValidationRule(error: "Field is required")
PrefixValidationRuleValidates string prefixPrefixValidationRule(prefix: "https://", error: "URL must start with https://")
SuffixValidationRuleValidates string suffixSuffixValidationRule(suffix: ".com", error: "Domain must end with .com")
RegexValidationRulePattern matching validationRegexValidationRule(pattern: "^\\d{3}-\\d{4}$", error: "Invalid phone format")
URLValidationRuleValidates URL formatURLValidationRule(error: "Please enter a valid URL")
CreditCardValidationRuleValidates credit card numbers (Luhn algorithm)CreditCardValidationRule(error: "Invalid card number")
EmailValidationRuleValidates email formatEmailValidationRule(error: "Please enter a valid email")
CharactersValidationRuleValidates that a string contains only characters from the allowed CharacterSetCharactersValidationRule(characterSet: .letters, error: "Invalid characters")
NilValidationRuleValidates that value is nilNilValidationRule(error: "Value must be nil")
PositiveNumberValidationRuleValidates that value is positivePositiveNumberValidationRule(error: "Value must be positive")
NoWhitespaceValidationRuleValidates that a string does not contain any whitespace charactersNoWhitespaceValidationRule(error: "Spaces are not allowed")
ContainsValidationRuleValidates that a string contains a specific substringContainsValidationRule(substring: "@", error: "Must contain @")
EqualityValidationRuleValidates that the input is equal to a given reference valueEqualityValidationRule(compareTo: password, error: "Passwords do not match")
ComparisonValidationRuleValidates that input against a comparison constraintComparisonValidationRule(greaterThan: 0, error: "Must be greater than 0")
IBANValidationRuleValidates that a string is a valid IBAN (International Bank Account Number)IBANValidationRule(error: "Invalid IBAN")
IPAddressValidationRuleValidates that a string is a valid IPv4 or IPv6 addressIPAddressValidationRule(version: .v4, error: ValidationError("Invalid IPv4"))
PostalCodeValidationRuleValidates postal/ZIP codes for different countriesPostalCodeValidationRule(country: .uk, error: "Invalid post code")
Base64ValidationRuleValidates that a string represents valid Base64-encoded data.Base64ValidationRule(error: "The input is not valid Base64.")
UUIDValidationRuleValidates UUID formatUUIDValidationRule(error: "Please enter a valid UUID")
JSONValidationRuleValidates that a string represents valid JSONJSONValidationRule(error: "Invalid JSON")

Custom Validators

Create custom validation rules by conforming to IValidationRule:

import ValidatorCore
structEmailDomainValidationRule:IValidationRule{typealiasInput=StringletallowedDomains:[String]leterror:IValidationErrorinit(allowedDomains:[String], error:IValidationError){self.allowedDomains = allowedDomains
self.error = error
}func validate(input:String)->Bool{guardlet domain = input.split(separator:"@").last else{returnfalse}return allowedDomains.contains(String(domain))}}
// Usage
letrule=EmailDomainValidationRule(
allowedDomains:["company.com","company.org"],
error:"Only company email addresses are allowed")

Composing Validators

Combine multiple validators for complex validation logic:

// Define reusable validation rules
letlengthRule=LengthValidationRule(
min:8,
max:128,
error:"Password must be 8-128 characters")letuppercaseRule=RegexValidationRule(
pattern:".*[A-Z].*",
error:"Must contain uppercase letter")letlowercaseRule=RegexValidationRule(
pattern:".*[a-z].*",
error:"Must contain lowercase letter")letnumberRule=RegexValidationRule(
pattern:".*[0-9].*",
error:"Must contain number")letspecialCharRule=RegexValidationRule(
pattern:".*[!@#$%^&*(),.?\":{}|<>].*",
error:"Must contain special character")
// UIKit: Pass all rules to your text field
passwordField.add(rules:[
lengthRule,
uppercaseRule,
lowercaseRule,
numberRule,
specialCharRule
])
// SwiftUI: Use in validation modifier
SecureField("Password", text: $password).validation($password, rules:[
lengthRule,
uppercaseRule,
lowercaseRule,
numberRule,
specialCharRule
]){ result inif case .invalid(let errors)= result {self.passwordErrors = errors
}}

Examples

You can find usage examples in the Examples directory of the repository.

These examples demonstrate how to integrate the package, configure validation rules,
and build real-world user interfaces using ValidatorCore and ValidatorUI.

Communication

Contributing

We love contributions! Please read our Contributing Guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes.

Development Setup

Bootstrap the development environment:

mise install

Code of Conduct

This project adheres to the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code.

Author

Nikita Vasilev

License

Validator is released under the MIT license. See LICENSE for details.


⬆ back to top

Made with ❤️ by space-code

Releases

Packages

Used by

Contributors

Languages