From 1288c10568ba4be89d804f68e07c849079f098de Mon Sep 17 00:00:00 2001 From: Jeff Potter Date: Thu, 20 Nov 2014 12:59:03 -0700 Subject: [PATCH] updating readme --- MyPlayground.playground/contents.xcplayground | 7 + MyPlayground.playground/section-1.swift | 10 + MyPlayground.playground/timeline.xctimeline | 6 + README.md | 202 +++++++++++++++++- Validator.xcodeproj/project.pbxproj | 78 +++++++ .../xcshareddata/Validator.xccheckout | 41 ++++ .../UserInterfaceState.xcuserstate | Bin 8536 -> 13100 bytes Validator/EmailValidation.swift | 26 +++ Validator/FullNameValidation.swift | 22 ++ Validator/MaxLengthValidation.swift | 20 ++ Validator/MinLengthValidation.swift | 20 ++ Validator/PasswordValidation.swift | 25 +++ Validator/PhoneNumberValidation.swift | 24 +++ Validator/RequiredValidation.swift | 19 ++ Validator/Validation.swift | 13 ++ Validator/ValidationError.swift | 20 ++ Validator/ValidationErrorType.swift | 43 ++++ Validator/ValidationFactory.swift | 32 +++ Validator/ValidationRule.swift | 31 +++ Validator/ValidationRuleType.swift | 20 ++ Validator/Validator.swift | 64 ++++++ Validator/ViewController.swift | 4 + Validator/ZipCodeValidation.swift | 24 +++ 23 files changed, 749 insertions(+), 2 deletions(-) create mode 100644 MyPlayground.playground/contents.xcplayground create mode 100644 MyPlayground.playground/section-1.swift create mode 100644 MyPlayground.playground/timeline.xctimeline create mode 100644 Validator.xcodeproj/project.xcworkspace/xcshareddata/Validator.xccheckout create mode 100644 Validator/EmailValidation.swift create mode 100644 Validator/FullNameValidation.swift create mode 100644 Validator/MaxLengthValidation.swift create mode 100644 Validator/MinLengthValidation.swift create mode 100644 Validator/PasswordValidation.swift create mode 100644 Validator/PhoneNumberValidation.swift create mode 100644 Validator/RequiredValidation.swift create mode 100644 Validator/Validation.swift create mode 100644 Validator/ValidationError.swift create mode 100644 Validator/ValidationErrorType.swift create mode 100644 Validator/ValidationFactory.swift create mode 100644 Validator/ValidationRule.swift create mode 100644 Validator/ValidationRuleType.swift create mode 100644 Validator/Validator.swift create mode 100644 Validator/ZipCodeValidation.swift diff --git a/MyPlayground.playground/contents.xcplayground b/MyPlayground.playground/contents.xcplayground new file mode 100644 index 0000000..8e39341 --- /dev/null +++ b/MyPlayground.playground/contents.xcplayground @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/MyPlayground.playground/section-1.swift b/MyPlayground.playground/section-1.swift new file mode 100644 index 0000000..b31d259 --- /dev/null +++ b/MyPlayground.playground/section-1.swift @@ -0,0 +1,10 @@ +// Playground - noun: a place where people can play + +import UIKit + +var str = "Hello, playground" + +var errors[String:Int] = ["This":1, "Is": 2, "A": 3, "Test": 4] + +println(errors.values) + diff --git a/MyPlayground.playground/timeline.xctimeline b/MyPlayground.playground/timeline.xctimeline new file mode 100644 index 0000000..bf468af --- /dev/null +++ b/MyPlayground.playground/timeline.xctimeline @@ -0,0 +1,6 @@ + + + + + diff --git a/README.md b/README.md index ebc6753..525a59b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,202 @@ -swift-validator +Swift-validator =============== -A rule-based validation library for Swift +Swift Validator is a rule-based validation library for Swift. + +Core Concepts + +* ```UITextField``` and ```ValidationRule``` go into the ```Validator```, ```UITextFields``` and ```ValidationErrors``` come out of ```Validator``` +* ```UITextField``` is registered to ```Validator``` +* ```Validator``` evaluates ```ValidationRules``` sequentially and stops evaluating when a ```ValidationRule``` fails. + +## Quick Start + +Initialize the ```Validator``` by setting a delegate to a View Controller or other object. + +```swift + +// ViewController.swift + +override func viewDidLoad() { + super.viewDidLoad() + + var validator = Validator(delegate: self) +} + +``` + +Register the fields that you want to validate + +```swift + +var fields:[String] = ["FullName", "Email", "Phone"] + +// Validation Rules are evaluated from left to right. The first rule is ValidationRuleType.Required the second is ValidationRuleType.FullName. +validator.registerField(fields[0], textField:nameTextField, rules: [.Required, .FullName]) +validator.registerField(fields[1], textField:emailTextField, rules: [.Required, .Email]) +validator.registerField(fields[2], textField:phoneTextField, rules: [.Required, .PhoneNumber]) + +``` + +Validate Individual Field + +```swift + +func validator.validateFieldBy(fields[0], delegate:self) + +// ValidationFieldDelegate methods +func validationFieldSuccess(key:String, validField:UITextField){ + validField.backgroundColor = UIColor.greenColor() +} + +func validationFieldFailure(key:String, error:ValidationError){ + println(error.error.description) +} + +``` + +Validate All Fields + +```swift + +validator.validateAllBy(fields, delegate:self) + +// ValidationDelegate methods + +func validationWasSuccessful(){ + // submit the form +} + +func validationFailed(key:String, errors[String:ValidationError]){ + // turn the fields to red + for error in errors.values { + error.textField.backgroundColor = UIColor.redColor() + println("error -> \(error.error.description)") + } +} + +``` + +## Custom Validation + +We will create a ```SSNValidation``` class to show how to create your own Validation. A United States Social Security Number (or SSN) is a field that consists of XXX-XX-XXXX. + +Create a class that implements the Validation protocol + +```swift + +class SSNValidation: Validation { + let SSN_REGEX = "^\\d{3}-\\d{2}-\\d{4}$" + + func validate(value: String) -> (Bool, ValidationErrorType) { + if let ssnTest = NSPredicate(format: "SELF MATCHES %@", SSN_REGEX) { + if ssnTest.evaluateWithObject(value) { + return (true, .NoError) + } + return (false, .SocialSecurity) // We will create this later ValidationErrorTYpe + } + return (false, .SocialSecurity) + } + +} + +``` + +Add the ```.SocialSecurity``` ValidationRuleType + +```swift + +enum ValidationRuleType { + case Required, + Email, + Password, + MinLength, + MaxLength, + ZipCode, + PhoneNumber, + FullName, + SocialSecurity // Added to the Rule Types +} + +``` + +Add the ```.SocialSecurity``` ValidationErrorType and description() + +```swift + +enum ValidationErrorType { + case Required, + Email, + Password, + MinLength, + MaxLength, + ZipCode, + PhoneNumber, + FullName, + SocialSecurity, // Added to the Error Types + NoError + + func description() -> String { + switch self { + case .Required: + return "Required field" + case .Email: + return "Must be a valid email" + case .MaxLength: + return "This field should be less than" + case .ZipCode: + return "5 digit zipcode" + case .PhoneNumber: + return "10 digit phone number" + case .Password: + return "Must be at least 8 characters" + case .FullName: + return "Provide a first & last name" + // Adding the desired error message + case .SocialSecurity: + return "SSN is XXX-XX-XXXX" + default: + return "" + } + } + +} + +``` +Register the Validation with the ValidationFactory + +```swift + +class ValidationFactory { + class func validationForRule(rule:ValidationRuleType) -> Validation { + switch rule { + case .Required: + return RequiredValidation() + case .Email: + return EmailValidation() + case .MinLength: + return MinLengthValidation() + case .MaxLength: + return MaxLengthValidation() + case .PhoneNumber: + return PhoneNumberValidation() + case .ZipCode: + return ZipCodeValidation() + case .FullName: + return FullNameValidation() + // Add Validation to allow Factory to create one on the fly for you + case .SocialSecurity: + return SSNValidation() + default: + return RequiredValidation() + } + } +} + +``` + + + + + + diff --git a/Validator.xcodeproj/project.pbxproj b/Validator.xcodeproj/project.pbxproj index 2625ab2..45990de 100644 --- a/Validator.xcodeproj/project.pbxproj +++ b/Validator.xcodeproj/project.pbxproj @@ -13,6 +13,21 @@ 62D1AE241A1E6D4400E4DFF8 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 62D1AE231A1E6D4400E4DFF8 /* Images.xcassets */; }; 62D1AE271A1E6D4400E4DFF8 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 62D1AE251A1E6D4400E4DFF8 /* LaunchScreen.xib */; }; 62D1AE331A1E6D4500E4DFF8 /* ValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE321A1E6D4500E4DFF8 /* ValidatorTests.swift */; }; + 62D1AE3E1A1E6FEF00E4DFF8 /* FullNameValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE3D1A1E6FEF00E4DFF8 /* FullNameValidation.swift */; }; + 62D1AE491A1E6FF800E4DFF8 /* EmailValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE3F1A1E6FF800E4DFF8 /* EmailValidation.swift */; }; + 62D1AE4A1A1E6FF800E4DFF8 /* MaxLengthValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE401A1E6FF800E4DFF8 /* MaxLengthValidation.swift */; }; + 62D1AE4B1A1E6FF800E4DFF8 /* MinLengthValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE411A1E6FF800E4DFF8 /* MinLengthValidation.swift */; }; + 62D1AE4C1A1E6FF800E4DFF8 /* PhoneNumberValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE421A1E6FF800E4DFF8 /* PhoneNumberValidation.swift */; }; + 62D1AE4D1A1E6FF800E4DFF8 /* RequiredValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE431A1E6FF800E4DFF8 /* RequiredValidation.swift */; }; + 62D1AE4E1A1E6FF800E4DFF8 /* Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE441A1E6FF800E4DFF8 /* Validation.swift */; }; + 62D1AE4F1A1E6FF800E4DFF8 /* ValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE451A1E6FF800E4DFF8 /* ValidationError.swift */; }; + 62D1AE501A1E6FF800E4DFF8 /* ValidationErrorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE461A1E6FF800E4DFF8 /* ValidationErrorType.swift */; }; + 62D1AE511A1E6FF800E4DFF8 /* ValidationFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE471A1E6FF800E4DFF8 /* ValidationFactory.swift */; }; + 62D1AE521A1E6FF800E4DFF8 /* ValidationRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE481A1E6FF800E4DFF8 /* ValidationRule.swift */; }; + 62D1AE571A1E700200E4DFF8 /* ValidationRuleType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE531A1E700200E4DFF8 /* ValidationRuleType.swift */; }; + 62D1AE581A1E700200E4DFF8 /* Validator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE541A1E700200E4DFF8 /* Validator.swift */; }; + 62D1AE591A1E700200E4DFF8 /* ZipCodeValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE551A1E700200E4DFF8 /* ZipCodeValidation.swift */; }; + 62D1AE5A1A1E700200E4DFF8 /* PasswordValidation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE561A1E700200E4DFF8 /* PasswordValidation.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -36,6 +51,22 @@ 62D1AE2C1A1E6D4500E4DFF8 /* ValidatorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ValidatorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 62D1AE311A1E6D4500E4DFF8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 62D1AE321A1E6D4500E4DFF8 /* ValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorTests.swift; sourceTree = ""; }; + 62D1AE3D1A1E6FEF00E4DFF8 /* FullNameValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullNameValidation.swift; sourceTree = ""; }; + 62D1AE3F1A1E6FF800E4DFF8 /* EmailValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailValidation.swift; sourceTree = ""; }; + 62D1AE401A1E6FF800E4DFF8 /* MaxLengthValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MaxLengthValidation.swift; sourceTree = ""; }; + 62D1AE411A1E6FF800E4DFF8 /* MinLengthValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinLengthValidation.swift; sourceTree = ""; }; + 62D1AE421A1E6FF800E4DFF8 /* PhoneNumberValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhoneNumberValidation.swift; sourceTree = ""; }; + 62D1AE431A1E6FF800E4DFF8 /* RequiredValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequiredValidation.swift; sourceTree = ""; }; + 62D1AE441A1E6FF800E4DFF8 /* Validation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validation.swift; sourceTree = ""; }; + 62D1AE451A1E6FF800E4DFF8 /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.swift; sourceTree = ""; }; + 62D1AE461A1E6FF800E4DFF8 /* ValidationErrorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationErrorType.swift; sourceTree = ""; }; + 62D1AE471A1E6FF800E4DFF8 /* ValidationFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationFactory.swift; sourceTree = ""; }; + 62D1AE481A1E6FF800E4DFF8 /* ValidationRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationRule.swift; sourceTree = ""; }; + 62D1AE531A1E700200E4DFF8 /* ValidationRuleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationRuleType.swift; sourceTree = ""; }; + 62D1AE541A1E700200E4DFF8 /* Validator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validator.swift; sourceTree = ""; }; + 62D1AE551A1E700200E4DFF8 /* ZipCodeValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipCodeValidation.swift; sourceTree = ""; }; + 62D1AE561A1E700200E4DFF8 /* PasswordValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordValidation.swift; sourceTree = ""; }; + 62D1AE5C1A1E78EE00E4DFF8 /* MyPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = MyPlayground.playground; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -59,6 +90,7 @@ 62D1AE0E1A1E6D4400E4DFF8 = { isa = PBXGroup; children = ( + 62D1AE5C1A1E78EE00E4DFF8 /* MyPlayground.playground */, 62D1AE191A1E6D4400E4DFF8 /* Validator */, 62D1AE2F1A1E6D4500E4DFF8 /* ValidatorTests */, 62D1AE181A1E6D4400E4DFF8 /* Products */, @@ -77,6 +109,7 @@ 62D1AE191A1E6D4400E4DFF8 /* Validator */ = { isa = PBXGroup; children = ( + 62D1AE3C1A1E6FAF00E4DFF8 /* lib */, 62D1AE1C1A1E6D4400E4DFF8 /* AppDelegate.swift */, 62D1AE1E1A1E6D4400E4DFF8 /* ViewController.swift */, 62D1AE201A1E6D4400E4DFF8 /* Main.storyboard */, @@ -112,6 +145,36 @@ name = "Supporting Files"; sourceTree = ""; }; + 62D1AE3C1A1E6FAF00E4DFF8 /* lib */ = { + isa = PBXGroup; + children = ( + 62D1AE5B1A1E701B00E4DFF8 /* Validations */, + 62D1AE531A1E700200E4DFF8 /* ValidationRuleType.swift */, + 62D1AE541A1E700200E4DFF8 /* Validator.swift */, + 62D1AE451A1E6FF800E4DFF8 /* ValidationError.swift */, + 62D1AE461A1E6FF800E4DFF8 /* ValidationErrorType.swift */, + 62D1AE471A1E6FF800E4DFF8 /* ValidationFactory.swift */, + 62D1AE481A1E6FF800E4DFF8 /* ValidationRule.swift */, + ); + name = lib; + sourceTree = ""; + }; + 62D1AE5B1A1E701B00E4DFF8 /* Validations */ = { + isa = PBXGroup; + children = ( + 62D1AE441A1E6FF800E4DFF8 /* Validation.swift */, + 62D1AE3D1A1E6FEF00E4DFF8 /* FullNameValidation.swift */, + 62D1AE421A1E6FF800E4DFF8 /* PhoneNumberValidation.swift */, + 62D1AE431A1E6FF800E4DFF8 /* RequiredValidation.swift */, + 62D1AE3F1A1E6FF800E4DFF8 /* EmailValidation.swift */, + 62D1AE411A1E6FF800E4DFF8 /* MinLengthValidation.swift */, + 62D1AE401A1E6FF800E4DFF8 /* MaxLengthValidation.swift */, + 62D1AE561A1E700200E4DFF8 /* PasswordValidation.swift */, + 62D1AE551A1E700200E4DFF8 /* ZipCodeValidation.swift */, + ); + name = Validations; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -212,8 +275,23 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 62D1AE4C1A1E6FF800E4DFF8 /* PhoneNumberValidation.swift in Sources */, + 62D1AE5A1A1E700200E4DFF8 /* PasswordValidation.swift in Sources */, + 62D1AE4F1A1E6FF800E4DFF8 /* ValidationError.swift in Sources */, + 62D1AE3E1A1E6FEF00E4DFF8 /* FullNameValidation.swift in Sources */, + 62D1AE4B1A1E6FF800E4DFF8 /* MinLengthValidation.swift in Sources */, 62D1AE1F1A1E6D4400E4DFF8 /* ViewController.swift in Sources */, + 62D1AE4E1A1E6FF800E4DFF8 /* Validation.swift in Sources */, 62D1AE1D1A1E6D4400E4DFF8 /* AppDelegate.swift in Sources */, + 62D1AE581A1E700200E4DFF8 /* Validator.swift in Sources */, + 62D1AE501A1E6FF800E4DFF8 /* ValidationErrorType.swift in Sources */, + 62D1AE491A1E6FF800E4DFF8 /* EmailValidation.swift in Sources */, + 62D1AE511A1E6FF800E4DFF8 /* ValidationFactory.swift in Sources */, + 62D1AE591A1E700200E4DFF8 /* ZipCodeValidation.swift in Sources */, + 62D1AE571A1E700200E4DFF8 /* ValidationRuleType.swift in Sources */, + 62D1AE521A1E6FF800E4DFF8 /* ValidationRule.swift in Sources */, + 62D1AE4A1A1E6FF800E4DFF8 /* MaxLengthValidation.swift in Sources */, + 62D1AE4D1A1E6FF800E4DFF8 /* RequiredValidation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Validator.xcodeproj/project.xcworkspace/xcshareddata/Validator.xccheckout b/Validator.xcodeproj/project.xcworkspace/xcshareddata/Validator.xccheckout new file mode 100644 index 0000000..ed01e81 --- /dev/null +++ b/Validator.xcodeproj/project.xcworkspace/xcshareddata/Validator.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + 41CB4003-1551-4C4C-B53F-78A56D1DFBE5 + IDESourceControlProjectName + Validator + IDESourceControlProjectOriginsDictionary + + 44BA2A1DF8B8E3EE0CCBE8F37625F38B9979A032 + github.com:jpotts18/swift-validator.git + + IDESourceControlProjectPath + Validator.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + 44BA2A1DF8B8E3EE0CCBE8F37625F38B9979A032 + ../.. + + IDESourceControlProjectURL + github.com:jpotts18/swift-validator.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + 44BA2A1DF8B8E3EE0CCBE8F37625F38B9979A032 + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 44BA2A1DF8B8E3EE0CCBE8F37625F38B9979A032 + IDESourceControlWCCName + Validator + + + + diff --git a/Validator.xcodeproj/project.xcworkspace/xcuserdata/jpotts18.xcuserdatad/UserInterfaceState.xcuserstate b/Validator.xcodeproj/project.xcworkspace/xcuserdata/jpotts18.xcuserdatad/UserInterfaceState.xcuserstate index 633be68baf27902f87180c0c7a3415f51c066970..5b4eb6eaf05f713a3dd9d197c279adae2114c90e 100644 GIT binary patch literal 13100 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 delta 5080 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 diff --git a/Validator/EmailValidation.swift b/Validator/EmailValidation.swift new file mode 100644 index 0000000..4711f4d --- /dev/null +++ b/Validator/EmailValidation.swift @@ -0,0 +1,26 @@ +// +// EmailValidation.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class EmailValidation: Validation { + + let EMAIL_REGEX = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}" + + func validate(value:String) -> (Bool, ValidationErrorType) { + + if let emailTest = NSPredicate(format: "SELF MATCHES %@", EMAIL_REGEX) { + if emailTest.evaluateWithObject(value) { + return (true, .NoError) + } else { + return (false,.Email) + } + } + return (false, .Email) + } +} \ No newline at end of file diff --git a/Validator/FullNameValidation.swift b/Validator/FullNameValidation.swift new file mode 100644 index 0000000..9e78993 --- /dev/null +++ b/Validator/FullNameValidation.swift @@ -0,0 +1,22 @@ +// +// FullNameValidation.swift +// Pingo +// +// Created by Jeff Potter on 11/19/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + + +class FullNameValidation : Validation { + + func validate(value:String) -> (Bool, ValidationErrorType) { + + var nameArray:[String] = split(value) { $0 == " " } + if nameArray.count == 2 { + return (true, .NoError) + } + return (false, .FullName) + } +} \ No newline at end of file diff --git a/Validator/MaxLengthValidation.swift b/Validator/MaxLengthValidation.swift new file mode 100644 index 0000000..bf9139f --- /dev/null +++ b/Validator/MaxLengthValidation.swift @@ -0,0 +1,20 @@ +// +// MaxLengthValidation.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class MaxLengthValidation: Validation { + let DEFAULT_MAX_LENGTH = 25 + + func validate(value: String) -> (Bool, ValidationErrorType) { + if countElements(value) > DEFAULT_MAX_LENGTH { + return (false, .MaxLength) + } + return (true, .NoError) + } +} \ No newline at end of file diff --git a/Validator/MinLengthValidation.swift b/Validator/MinLengthValidation.swift new file mode 100644 index 0000000..ce4078e --- /dev/null +++ b/Validator/MinLengthValidation.swift @@ -0,0 +1,20 @@ +// +// MinLengthValidation.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class MinLengthValidation: Validation { + let DEFAULT_MIN_LENGTH = 3 + + func validate(value: String) -> (Bool, ValidationErrorType) { + if countElements(value) < DEFAULT_MIN_LENGTH { + return (false, .MinLength) + } + return (true, .NoError) + } +} \ No newline at end of file diff --git a/Validator/PasswordValidation.swift b/Validator/PasswordValidation.swift new file mode 100644 index 0000000..1583dcc --- /dev/null +++ b/Validator/PasswordValidation.swift @@ -0,0 +1,25 @@ +// +// PasswordValidation.swift +// Pingo +// +// Created by Jeff Potter on 11/13/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class PasswordValidation : Validation { + + // 8 characters. one uppercase + var PASSWORD_REGEX = "^(?=.*?[A-Z]).{8,}$" + + func validate(value: String) -> (Bool, ValidationErrorType) { + if let passwordTes = NSPredicate(format: "SELF MATCHES %@", PASSWORD_REGEX) { + if passwordTes.evaluateWithObject(value) { + return (true, .NoError) + } + return (false, .Password) + } + return (false, .Password) + } +} \ No newline at end of file diff --git a/Validator/PhoneNumberValidation.swift b/Validator/PhoneNumberValidation.swift new file mode 100644 index 0000000..7f56ca8 --- /dev/null +++ b/Validator/PhoneNumberValidation.swift @@ -0,0 +1,24 @@ +// +// PhoneValidation.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class PhoneNumberValidation: Validation { + let PHONE_REGEX = "^\\d{3}-\\d{3}-\\d{4}$" + + func validate(value: String) -> (Bool, ValidationErrorType) { + if let phoneTest = NSPredicate(format: "SELF MATCHES %@", PHONE_REGEX) { + if phoneTest.evaluateWithObject(value) { + return (true, .NoError) + } + return (false, .PhoneNumber) + } + return (false, .PhoneNumber) + } + +} diff --git a/Validator/RequiredValidation.swift b/Validator/RequiredValidation.swift new file mode 100644 index 0000000..0570d69 --- /dev/null +++ b/Validator/RequiredValidation.swift @@ -0,0 +1,19 @@ +// +// RequiredValidation.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class RequiredValidation: Validation { + + func validate(value:String) -> (Bool, ValidationErrorType) { + if value.isEmpty { + return (false, .Required) + } + return (true, .NoError) + } +} \ No newline at end of file diff --git a/Validator/Validation.swift b/Validator/Validation.swift new file mode 100644 index 0000000..c7b5c45 --- /dev/null +++ b/Validator/Validation.swift @@ -0,0 +1,13 @@ +// +// Validation.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +protocol Validation { + func validate(value:String) -> (Bool, ValidationErrorType) +} \ No newline at end of file diff --git a/Validator/ValidationError.swift b/Validator/ValidationError.swift new file mode 100644 index 0000000..1b19384 --- /dev/null +++ b/Validator/ValidationError.swift @@ -0,0 +1,20 @@ +// +// File.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class ValidationError { + let textField:UITextField + let error:ValidationErrorType + + init(textField:UITextField, error:ValidationErrorType){ + self.textField = textField + self.error = error + } + +} \ No newline at end of file diff --git a/Validator/ValidationErrorType.swift b/Validator/ValidationErrorType.swift new file mode 100644 index 0000000..f612776 --- /dev/null +++ b/Validator/ValidationErrorType.swift @@ -0,0 +1,43 @@ +// +// ValidationErrorType.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +enum ValidationErrorType { + case Required, + Email, + Password, + MinLength, + MaxLength, + ZipCode, + PhoneNumber, + FullName, + NoError + + func description() -> String { + switch self { + case .Required: + return "Required field" + case .Email: + return "Must be a valid email" + case .MaxLength: + return "This field should be less than" + case .ZipCode: + return "5 digit zipcode" + case .PhoneNumber: + return "10 digit phone number" + case .Password: + return "Must be at least 8 characters" + case .FullName: + return "Provide a first & last name" + default: + return "" + } + } + +} \ No newline at end of file diff --git a/Validator/ValidationFactory.swift b/Validator/ValidationFactory.swift new file mode 100644 index 0000000..fa73f4e --- /dev/null +++ b/Validator/ValidationFactory.swift @@ -0,0 +1,32 @@ +// +// Validations.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class ValidationFactory { + class func validationForRule(rule:ValidationRuleType) -> Validation { + switch rule { + case .Required: + return RequiredValidation() + case .Email: + return EmailValidation() + case .MinLength: + return MinLengthValidation() + case .MaxLength: + return MaxLengthValidation() + case .PhoneNumber: + return PhoneNumberValidation() + case .ZipCode: + return ZipCodeValidation() + case .FullName: + return FullNameValidation() + default: + return RequiredValidation() + } + } +} diff --git a/Validator/ValidationRule.swift b/Validator/ValidationRule.swift new file mode 100644 index 0000000..bf108cd --- /dev/null +++ b/Validator/ValidationRule.swift @@ -0,0 +1,31 @@ +// +// ValidationRule.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class ValidationRule { + let textField:UITextField + var rules:[ValidationRuleType] = [] + + init(textField:UITextField, rules:[ValidationRuleType]){ + self.textField = textField + self.rules = rules + } + + func validateField() -> ValidationError? { + for rule in rules { + var validation = ValidationFactory.validationForRule(rule) + var attempt:(isValid:Bool, error:ValidationErrorType) = validation.validate(textField.text) + if !attempt.isValid { + return ValidationError(textField: textField, error: attempt.error) + } + } + return nil + } + +} \ No newline at end of file diff --git a/Validator/ValidationRuleType.swift b/Validator/ValidationRuleType.swift new file mode 100644 index 0000000..d9dcd62 --- /dev/null +++ b/Validator/ValidationRuleType.swift @@ -0,0 +1,20 @@ +// +// ValidationRuleType.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +enum ValidationRuleType { + case Required, + Email, + Password, + MinLength, + MaxLength, + ZipCode, + PhoneNumber, + FullName +} \ No newline at end of file diff --git a/Validator/Validator.swift b/Validator/Validator.swift new file mode 100644 index 0000000..78f4ae3 --- /dev/null +++ b/Validator/Validator.swift @@ -0,0 +1,64 @@ +// +// Validator.swift +// Pingo +// +// Created by Jeff Potter on 11/10/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +protocol ValidationDelegate { + func validationWasSuccessful() + func validationFailed(errors:[String:ValidationError]) +} + +protocol ValidationFieldDelegate { + func validationFieldFailed(key:String, error:ValidationError) + func validationFieldSuccess(key:String, validField:UITextField) +} + +class Validator { + var validationRules:[String:ValidationRule] = [:] + var validationErrors:[String:ValidationError] = [:] + let delegate:ValidationDelegate + + init(delegate:ValidationDelegate){ + self.delegate = delegate + } + + func registerField(key:String, textField:UITextField, rules:[ValidationRuleType]) { + validationRules[key] = ValidationRule(textField: textField, rules: rules) + } + + func validateFieldByKey(key:String, delegate:ValidationFieldDelegate) { + if let currentRule:ValidationRule = validationRules[key] { + if var error:ValidationError = currentRule.validateField() { + delegate.validationFieldFailed(key, error:error) + } else { + delegate.validationFieldSuccess(key, validField:currentRule.textField) + } + } + } + + func validateAllBy(keys:[String], delegate:ValidationDelegate){ + + for key in keys { + if let currentRule:ValidationRule = validationRules[key] { + if var error:ValidationError = currentRule.validateField() { + validationErrors[key] = error + } else { + validationErrors.removeValueForKey(key) + } + } + } + + if validationErrors.isEmpty { + delegate.validationWasSuccessful() + } else { + delegate.validationFailed(validationErrors) + } + + } + +} \ No newline at end of file diff --git a/Validator/ViewController.swift b/Validator/ViewController.swift index 8e2d68d..a757aa0 100644 --- a/Validator/ViewController.swift +++ b/Validator/ViewController.swift @@ -13,11 +13,15 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. + + var validator = Validator(delegate: self) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. + + } diff --git a/Validator/ZipCodeValidation.swift b/Validator/ZipCodeValidation.swift new file mode 100644 index 0000000..af62b54 --- /dev/null +++ b/Validator/ZipCodeValidation.swift @@ -0,0 +1,24 @@ +// +// ZipCodeValidation.swift +// Pingo +// +// Created by Jeff Potter on 11/11/14. +// Copyright (c) 2014 Byron Mackay. All rights reserved. +// + +import Foundation + +class ZipCodeValidation: Validation { + let ZIP_REGEX = "\\d{5}" + + func validate(value: String) -> (Bool, ValidationErrorType) { + if let zipTest = NSPredicate(format: "SELF MATCHES %@", ZIP_REGEX) { + if zipTest.evaluateWithObject(value) { + return (true, .NoError) + } + return (false, .ZipCode) + } + return (false, .ZipCode) + } + +} \ No newline at end of file