{"id":22092928,"url":"https://github.com/altayer-digital/atgvalidator","last_synced_at":"2025-07-24T13:05:26.694Z","repository":{"id":56901151,"uuid":"186398320","full_name":"altayer-digital/ATGValidator","owner":"altayer-digital","description":"iOS validation framework with form validation support","archived":false,"fork":false,"pushed_at":"2020-03-11T10:47:22.000Z","size":123,"stargazers_count":51,"open_issues_count":2,"forks_count":9,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-11-14T05:34:54.524Z","etag":null,"topics":["credit-card","creditcard-validator","email-validation","form","form-validation","form-validator","ios","luhn-algorithm","password-strength","password-validation","regex","rule","rule-based","string-matching","swift","textfield","textfield-validation","validation","validation-library","validator"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/altayer-digital.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-13T10:30:24.000Z","updated_at":"2024-04-03T07:37:11.000Z","dependencies_parsed_at":"2022-08-20T18:20:35.759Z","dependency_job_id":null,"html_url":"https://github.com/altayer-digital/ATGValidator","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altayer-digital%2FATGValidator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altayer-digital%2FATGValidator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altayer-digital%2FATGValidator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/altayer-digital%2FATGValidator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/altayer-digital","download_url":"https://codeload.github.com/altayer-digital/ATGValidator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227476034,"owners_count":17779417,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["credit-card","creditcard-validator","email-validation","form","form-validation","form-validator","ios","luhn-algorithm","password-strength","password-validation","regex","rule","rule-based","string-matching","swift","textfield","textfield-validation","validation","validation-library","validator"],"created_at":"2024-12-01T03:11:55.307Z","updated_at":"2024-12-01T03:11:55.909Z","avatar_url":"https://github.com/altayer-digital.png","language":"Swift","readme":"# ATGValidator\n\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![CocoaPods](https://img.shields.io/cocoapods/v/ATGValidator.svg)](https://cocoapods.org/pods/ATGValidator)\n[![GitHub issues](https://img.shields.io/github/issues/altayer-digital/ATGValidator.svg)](https://github.com/altayer-digital/ATGValidator/issues)\n[![GitHub license](https://img.shields.io/github/license/altayer-digital/ATGValidator.svg)](https://github.com/altayer-digital/ATGValidator/blob/master/LICENSE.md)\n\nATGValidator is a validation framework written to address most common issues faced while verifying user input data. \n\nYou can use it to validate different data types directly, or validate ui components like UITextfield or UITextView, or even add validation support to your custom UI elements. You don't need to subclass native components to get the validation support. UITextField and UITextView has the support out of the box, adding support for any other elements is as simple as adding an extension with protocol conformance to `ValidatableInterface`.\n\nBest of all, you will get a **form validator** which consolidates validation results of all ui components added to it.\n\n### Sample Project\n\n![](https://i.imgur.com/1TWGG3z.gif)\n\nYou can find a sample project with form validation in [here](https://github.com/altayer-digital/examples/raw/master/iOS/FormValidationSample_CompleteProject.zip).\n\n## Installation\n\n#### Carthage\n\n`ATGValidator` can be installed using [Carthage](https://github.com/Carthage/Carthage). To do so simply add the following line to your `Cartfile`;\n\n```\ngithub \"altayer-digital/ATGValidator\" ~\u003e 1.0.0\n```\n\n#### Cocoapods\n\nTo use `ATGValidator` with cocoapods, add the following line to the `Podfile`;\n\n```\npod 'ATGValidator', '~\u003e 1.0'\n```\n## Usage\nFirst step to add validation is to set the validation rules on the ui element. Any UI element conforming to `ValidatableInterface` accepts an array of rules.\n```swift\ntextfield.validationRules = [\n    CharacterSetRule.containsUpperCase(),\n    CharacterSetRule.containsLowerCase(),\n    CharacterSetRule.containsNumber(),\n    CharacterSetRule.containsSymbols()\n]\n```\n\nIn order to validate a single textfield or textview, you can set a `validationHandler` closure on the textfield/textview. This will be executed whenever validation is done on the field and result is ready to be applied. An array of errors are passed with result object if the validation result is a failure.\n```swift\ntextfield.validationHandler = { result in\n    print(result.status, result.errors)\n}\n```\nYou can call `validateOnInputChange` or `validateOnFocusLoss` on the ui element to make it perform validations on the corresponding events.\n```swift\ntextfield.validateOnInputChange(true)\ntextfield.validateOnFocusLoss(true)\n```\n\nIf you need to aggregate validations of multiple ui elements, you need to create a `FormValidator` instance, and add all needed elements to the form validator.\n```swift\nlet formValidator = FormValidator(handler: { result in\n    print(result.status, result.errors)\n})\nformValidator.add(textfield)\n```\nWhen adding an item to form validator, by default validation will be done on focus loss on those items. But you can change this behaviour for individual fields by mentioning validationPolicy while adding the item to form validator.\n```swift\nformValidator.add(textfield, policy: .onInputChange) // or .onFocusLoss or .none\n```\nWhenever the fields have corresponding state changes to their data, form validator's handler closure will be executed with aggregated results automatically.\n\nIf you want to perform validation on-demand, you can use the method `validateForm(shouldInvokeElementHandlers:completion:)` to do it as and when you need it.\n\n## Rules\nThere are 6 types of rules readily available with the framework out of the box. For all rules you can set a custom error while initializing, or call helper methods `with(error:)-\u003eRule` or `with(errorMessage:)-\u003eRule` to set it later. If not, all rules have their own default errors from `ValidationError` enum. You can find them below with each rule description.\n\n#### EqualityRule\nThis rule is used to check if the value of the type is equal to the supplied value.\n\nYou can create an equality rule by passing the value to be checked against, to the initializer. Optionally you can pass in the mode (`equal`, `notEqual`) and an error object to be returned in case of validation failure.\n```swift\nlet rule = EqualityRule(value: \"text_to_be_verified\")\n// or \nlet rule = EqualityRule(value: \"shouldn't_be_equal_text\", mode: .notEqual, error: ValidationError.equal)\n```\nDefault errors: \n* `notEqual` error for `equal` mode.\n* `equal` error for `notEqual` mode.\n\n#### RangeRule\nYou can check if a value is in a specific range with this rule.\n\n```swift\nlet rangeRule = RangeRule(min: 200, max: 299)\n```\nDefault error: `valueOutOfRange`\n\n#### StringLengthRule\nYou can check for a string's length conformance using this rule. You can pass in whether to trim white spaces and to ignore specific charactersets.\n```swift\nlet lengthInRangeRule = StringLengthRule(min: 5, max: 10, trimWhiteSpace: true, ignoreCharactersIn: CharacterSet.symbols)\nlet minLengthRule = StringLengthRule.min(5)\nlet maxLengthRule = StringLengthRule.max(5)\nlet equalLengthRule = StringLengthRule.equal(to: 6)\nlet requiredStringRule = StringLengthRule.required()\n```\nDefault errors: \n* default: `lengthOutOfRange`\n* min: `shorterThanMinimumLength`\n* max: `longerThanMaximumLength`\n* equal: `notEqual`\n* required: `shorterThanMinimumLength`\n\n#### StringRegexRule\nThis rule allows you to perform regular expression matching on strings. You can optionally pass in whether to trim white spaces.\n```swift\nlet regexRule = StringRegexRule(regex: \"^[0-9]*$\")\nlet emailRule = StringRegexRule.email\nlet containsNumber = StringRegexRule.containsNumber()\nlet containsNumbersMinMax = StringRegexRule.containsNumber(min: 2, max: 4)\nlet containsUpperCase = StringRegexRule.containsUpperCase()\nlet containsUpperCaseMinMax = StringRegexRule.containsUpperCase(min: 1, max: 2)\nlet containsLowerCase = StringRegexRule.containsLowerCase()\nlet containsLowerCaseMinMax = StringRegexRule.containsLowerCase(min: 1, max: 2)\nlet numbersOnly = StringRegexRule.numbersOnly\nlet lowerCaseOnly = StringRegexRule.lowerCaseOnly\nlet upperCaseOnly = StringRegexRule.upperCaseOnly\n```\nDefault errors: \n* default: `regexMismatch`\n* email: `invalidEmail`\n* containsNumber: `numberNotFound`\n* containsUpperCase: `upperCaseNotFound`\n* containsLowerCase: `lowerCaseNotFound`\n* numbersOnly: `invalidType`\n* lowerCaseOnly: `invalidType`\n* upperCaseOnly: `invalidType`\n\n#### StringValueMatchRule\nYou can use this rule to check if values from 2 textfields are same. An ideal example is when we check if password field and confirm password field have same contents.\n```swift\nlet passwordTextfield: UITextField?\nlet confirmPasswordTextfield: UITextField?\nlet valueMatchRule = StringValueMatchRule(base: passwordTextfield)\nconfirmPasswordTextfield.validationRules = [valueMatchRule]\n```\nDefault error: `notEqual`\n\n#### PaymentCardRule\nPayment card rule can be used to check if a supplied card number is a valid payment card number. Luhn's algorithm in combination with regex matching is used to check the validity of the provided card numbers. The rule can be initiated with card types to be checked for, or if not passed will check for all available card types. Available card types are;\n* American Express\n* MasterCard\n* Maestro\n* Visa\n* Visa Electron\n* Discover\n* Diners Club\n\n```swift\nlet cardRule = PaymentCardRule(acceptedTypes: [.amex, .mastercard, .visa, .discover])\n// or\nlet cardRule = PaymentCardRule()\ntextfield.validationRules = [cardRule]\ntextfield.validationHandler = { result in\n    if let suggestedType = result.value as? PaymentCardType {\n        // This is a suggestion from the framework from the input you entered.\n        // Please note that having a suggested card type does not mean it's validation is success.\n        // You need to handle success/failure separately outside this confition.\n    }\n}\ntextfield.validateOnInputChange(true)\n```\nDefault errors:\n* If card number is not valid: `invalidPaymentCardNumber`\n* If card number is valid, but is not one of the accepted types: `paymentCardNotSupported`\n\nIn order to identify the card type while you are typing in the number, please use the `value` field in `Result` object. If a card type can be suggested from the entered input, the rule will populate the `Result.value` field with the card type, else it will be populated with the input.\nPlease note that a minimum of 4 characters needs to be entered before the rule starts finding card type suggestions.\n\n## Advanced Usage\n\nYou can validate common data types out of the box as illustrated below;\n\n```swift\n\"Example with 1 number\".satisfyAll(rules: [CharacterSetRule.containsNumber()]).status   // success\n\"Example with more than 20 characters\".satisfyAll(rules: [StringLengthRule.max(20)]).status       // failure\n472.satisfyAll(rules: [EqualityRule(value: 472.5)])                                     // failure\n301.satisfyAny(rules: [RangeRule(min: 200, max: 299), EqualityRule(value: 304)])        // failure\n200.satisfyAny(rules: [RangeRule(min: 200, max: 299), EqualityRule(value: 304)])        // success\n```\n\nAs for validating contents of a ui component, please see the example below;\n\n```swift\ntextfield.validationRules = [CharacterSetRule.lowerCaseOnly(ignoreCharactersIn: .whitespaces)]\ntextfield.validationHandler = { result in\n    // This block will be executed with relevant result whenever validation is done.\n    print(result.status)    // success\n}\n// Below line is to manually trigger validation.\ntextfield.validateTextField()\n```\n\nIn order to use form validator, we need to create a `FormValidator` instance and add required ui elements to the form validator. We can either call the validateForm method directly, or associate a handler closure to the form validator, which will be called when any of the added ui elements' value changes.\n```swift\nlet formValidator = FormValidator()\nformValidator.add(textfield)\n// Add more ui elements here.\nformValidator.validateForm { result in\n    print(result)\n}\n```\n\n### Validatable\n Validatable is the base protocol which is conformed by any data type that can be validated. Most commonly used types in Swift conforms to Validatable out-of-the-box. Below is the list of all data types that conforms to Validatable out-of-the-box.\n\n * String\n * Bool\n * Int\n * Double\n * Float\n * CGFloat\n * Date\n\nIf you want to add validation support to any other types, you can do so by conforming it to the validatable protocol.\n\n### ValidatableInterface\nIf any custom UI element needs to support validation, it needs to conform to `ValidatableInterface` protocol. Out of the box, ATGValidator has added this conformance to `UITextField` and `UITextView`. Please note that the `ValidatableInterface` protocol conforms to `Validatable` protocol. Please see the example for `UITextField` below to understand how the conformance needs to be implemented.\n```swift\nextension UITextField: ValidatableInterface {\n\n    public var inputValue: Any {\n        return text ?? \"\"\n    }\n\n    public func validateOnInputChange(_ validate: Bool) {\n\n        if validate {\n            addTarget(self, action: #selector(validateTextField), for: .editingChanged)\n        } else {\n            removeTarget(self, action: #selector(validateTextField), for: .editingChanged)\n        }\n    }\n\n    public func validateOnFocusLoss(_ validate: Bool) {\n\n        if validate {\n            addTarget(self, action: #selector(validateTextField), for: .editingDidEnd)\n            addTarget(self, action: #selector(validateTextField), for: .editingDidEndOnExit)\n        } else {\n            removeTarget(self, action: #selector(validateTextField), for: .editingDidEnd)\n            removeTarget(self, action: #selector(validateTextField), for: .editingDidEndOnExit)\n        }\n    }\n\n    @objc public func validateTextField() {\n\n        guard let rules = validationRules else {\n            return\n        }\n\n        var result = satisfyAll(rules: rules)\n        if result.status == .success {\n            validValue = result.value\n        } else if let value = validValue {\n            result.value = value\n        }\n        validationHandler?(result)\n        formHandler?(result)\n    }\n}\n```\n\n## Custom Errors\n`ValidationError` is an enum that holds all default validation errors. If you don't want to define an additional error object, and just needs a custom validation error string to be passed, please use `ValidationError.custom(errorMessage: String)` for it. \nThere are couple of helper methods in `Rule` to set custom errors easily. They are as given below;\n```swift\nvar emailRule = StringRegexRule.email\nemailRule = emailRule.with(error: CustomErrorThatConformsToErrorProtocol())\n// or\nemailRule = emailRule.with(errorMessage: \"Email is not correct..!\")\n\n```\n\n## ValidatorCache\nThis is a custom in memory storage used to hold all rules, form handler and validation handlers closures. Please feel free to explore how it is implemented.\n\n## Due Credits\n\nWhen we were faced with the task of finding a good validation framework to be used in our apps, we went through the exploration stage to find available opensource libraries and finally came up with the best one available for the job. If you were in the same situation, you must know which one it is. It's none other than the amazing [Validator](https://github.com/adamwaite/Validator) framework written by [Adam Waite](https://github.com/adamwaite). If you haven't seen it already, please head over to that repo and take a look.\n\n[Validator](https://github.com/adamwaite/Validator) uses generics extensively and is the backbone of how the framework works. This made it impossible for us to club together various ui elements and get a unified result for the validations from them. And we really needed form validation in our projects. \n\nSo we started writing a framework with protocols as its backbone and to use form validation as the main goal we wanted to achieve. And here is the result, ATGValidator. Please note that the core concepts are heavily influenced by [Validator](https://github.com/adamwaite/Validator) framework and we want to give the due credit to [Adam Waite](https://github.com/adamwaite) for the excellent work he has done.\n\n## Copyright and License\n\nATGValidator is available under the MIT license. See [`LICENSE.md`](https://github.com/altayer-digital/ATGValidator/blob/master/LICENSE.md) for more information.\n\n## Contributors\n\n[List of contributors is available through GitHub.](https://github.com/altayer-digital/ATGValidator/graphs/contributors)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faltayer-digital%2Fatgvalidator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faltayer-digital%2Fatgvalidator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faltayer-digital%2Fatgvalidator/lists"}