updating readme

This commit is contained in:
Jeff Potter 2014-11-20 12:59:03 -07:00
parent e2d88eeca0
commit 1288c10568
23 changed files with 749 additions and 2 deletions

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='3.0' sdk='iphonesimulator'>
<sections>
<code source-file-name='section-1.swift'/>
</sections>
<timeline fileName='timeline.xctimeline'/>
</playground>

View File

@ -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)

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Timeline
version = "3.0">
<TimelineItems>
</TimelineItems>
</Timeline>

202
README.md
View File

@ -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()
}
}
}
```

View File

@ -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 = "<group>"; };
62D1AE321A1E6D4500E4DFF8 /* ValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValidatorTests.swift; sourceTree = "<group>"; };
62D1AE3D1A1E6FEF00E4DFF8 /* FullNameValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullNameValidation.swift; sourceTree = "<group>"; };
62D1AE3F1A1E6FF800E4DFF8 /* EmailValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailValidation.swift; sourceTree = "<group>"; };
62D1AE401A1E6FF800E4DFF8 /* MaxLengthValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MaxLengthValidation.swift; sourceTree = "<group>"; };
62D1AE411A1E6FF800E4DFF8 /* MinLengthValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinLengthValidation.swift; sourceTree = "<group>"; };
62D1AE421A1E6FF800E4DFF8 /* PhoneNumberValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhoneNumberValidation.swift; sourceTree = "<group>"; };
62D1AE431A1E6FF800E4DFF8 /* RequiredValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequiredValidation.swift; sourceTree = "<group>"; };
62D1AE441A1E6FF800E4DFF8 /* Validation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validation.swift; sourceTree = "<group>"; };
62D1AE451A1E6FF800E4DFF8 /* ValidationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationError.swift; sourceTree = "<group>"; };
62D1AE461A1E6FF800E4DFF8 /* ValidationErrorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationErrorType.swift; sourceTree = "<group>"; };
62D1AE471A1E6FF800E4DFF8 /* ValidationFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationFactory.swift; sourceTree = "<group>"; };
62D1AE481A1E6FF800E4DFF8 /* ValidationRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationRule.swift; sourceTree = "<group>"; };
62D1AE531A1E700200E4DFF8 /* ValidationRuleType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidationRuleType.swift; sourceTree = "<group>"; };
62D1AE541A1E700200E4DFF8 /* Validator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Validator.swift; sourceTree = "<group>"; };
62D1AE551A1E700200E4DFF8 /* ZipCodeValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipCodeValidation.swift; sourceTree = "<group>"; };
62D1AE561A1E700200E4DFF8 /* PasswordValidation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordValidation.swift; sourceTree = "<group>"; };
62D1AE5C1A1E78EE00E4DFF8 /* MyPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = MyPlayground.playground; sourceTree = "<group>"; };
/* 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 = "<group>";
};
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 = "<group>";
};
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 = "<group>";
};
/* 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;
};

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>41CB4003-1551-4C4C-B53F-78A56D1DFBE5</string>
<key>IDESourceControlProjectName</key>
<string>Validator</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>44BA2A1DF8B8E3EE0CCBE8F37625F38B9979A032</key>
<string>github.com:jpotts18/swift-validator.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>Validator.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>44BA2A1DF8B8E3EE0CCBE8F37625F38B9979A032</key>
<string>../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>github.com:jpotts18/swift-validator.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>44BA2A1DF8B8E3EE0CCBE8F37625F38B9979A032</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>44BA2A1DF8B8E3EE0CCBE8F37625F38B9979A032</string>
<key>IDESourceControlWCCName</key>
<string>Validator</string>
</dict>
</array>
</dict>
</plist>

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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 ""
}
}
}

View File

@ -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()
}
}
}

View File

@ -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
}
}

View File

@ -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
}

64
Validator/Validator.swift Normal file
View File

@ -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)
}
}
}

View File

@ -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.
}

View File

@ -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)
}
}