Changing to version 2

This commit is contained in:
Jeff Potter 2015-03-06 14:59:06 -07:00
parent 658c7365c0
commit abc729aeac
27 changed files with 470 additions and 549 deletions

141
README.md
View File

@ -7,15 +7,15 @@ Swift Validator is a rule-based validation library for Swift.
## Core Concepts
* ```UITextField``` + ```ValidationRule``` go into ```Validator```
* ```UITextField``` + ```ValidationError``` come out of ```Validator```
* ```UITextField``` is registered to ```Validator```
* ```Validator``` evaluates ```ValidationRules``` sequentially and stops evaluating when a ```ValidationRule``` fails.
* ``UITextField`` + ``ValidationRule`` go into ```Validator``
* ``ITextField`` + ``ValidationError`` come out of ```Validator``
* ``UITextField`` is registered to ``Validator``
* ``Validator`` evaluates ``ValidationRules`` sequentially and stops evaluating when a ``ValidationRule`` fails.
* Keys are used to allow field registration in TableViewControllers and complex view hierarchies
## Quick Start
Initialize the ```Validator``` by setting a delegate to a View Controller or other object.
Initialize the ``Validator`` by setting a delegate to a View Controller or other object.
```swift
@ -33,31 +33,22 @@ Register the fields that you want to validate
```swift
var fields:[String] = ["FullName", "Email", "Phone"]
// Validation Rules are evaluated from left to right.
validator.registerField(fullNameTextField, rules: [RequiredRule(), FullNameRule()])
// Validation Rules are evaluated from left to right. The first rule is ValidationRuleType.Required the second is ValidationRuleType.FullName.
validator.registerFieldByKey(fields[0], textField:nameTextField, rules: [.Required, .FullName])
validator.registerFieldByKey(fields[1], textField:emailTextField, rules: [.Required, .Email])
validator.registerFieldByKe(fields[2], textField:phoneTextField, rules: [.Required, .PhoneNumber])
// You can pass in error labels with your rules
validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule()])
// You can validate against other fields
validator.registerField(emailConfirmTextField, errorLabel: emailConfirmErrorLabel, rules: [RequiredRule(), EmailRule(), ConfirmationRule(confirmField: emailTextField)])
// You can now pass in regex and length parameters through overloaded contructors
validator.registerField(phoneNumberTextField, errorLabel: phoneNumberErrorLabel, rules: [RequiredRule(), MinLengthRule(length: 9)])
validator.registerField(zipcodeTextField, errorLabel: zipcodeErrorLabel, rules: [RequiredRule(), ZipCodeRule()])
```
Validate Individual Field
```swift
validator.validateFieldByKey(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
@ -104,109 +95,11 @@ class SSNValidation: Validation {
}
```
Add the ```ValidationRuleType.SocialSecurity```
```swift
enum ValidationRuleType {
case Required,
Email,
Password,
MinLength,
MaxLength,
ZipCode,
PhoneNumber,
FullName,
SocialSecurity // Added to the ValidationRuleTypes
}
```
Add the ```ValidationErrorType.SocialSecurity``` and ```description()```
```swift
enum ValidationErrorType {
case Required,
Email,
Password,
MinLength,
MaxLength,
ZipCode,
PhoneNumber,
FullName,
SocialSecurity, // Added to the ValidationErrorTypes
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 ```SSNValidation``` 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()
}
}
}
```
Credits
-------
Swift Validator is written and maintained by Jeff Potter [@jpotts18](http://twitter.com/jpotts18) and friends.
Currently funded and maintained by [RingSeven](http://ringseven.com)
![RingSeven](https://avatars1.githubusercontent.com/u/8309133?v=3&s=200)
## Contributing
1. [Fork it](https://github.com/jpotts18/swift-validator/fork)

View File

@ -7,27 +7,23 @@
objects = {
/* Begin PBXBuildFile section */
628637261AAA474B00BC8FCF /* MinLengthRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 628637251AAA474B00BC8FCF /* MinLengthRule.swift */; };
628637281AAA49E300BC8FCF /* ConfirmRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 628637271AAA49E300BC8FCF /* ConfirmRule.swift */; };
62D1AE1D1A1E6D4400E4DFF8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE1C1A1E6D4400E4DFF8 /* AppDelegate.swift */; };
62D1AE1F1A1E6D4400E4DFF8 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62D1AE1E1A1E6D4400E4DFF8 /* ViewController.swift */; };
62D1AE221A1E6D4400E4DFF8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 62D1AE201A1E6D4400E4DFF8 /* Main.storyboard */; };
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 */; };
62DC8D651AAA42520095DFA7 /* Rule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DC8D641AAA42520095DFA7 /* Rule.swift */; };
62DC8D681AAA42920095DFA7 /* FullNameRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DC8D671AAA42920095DFA7 /* FullNameRule.swift */; };
62DC8D6C1AAA42CE0095DFA7 /* EmailRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DC8D691AAA42CE0095DFA7 /* EmailRule.swift */; };
62DC8D6D1AAA42CE0095DFA7 /* RequiredRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DC8D6A1AAA42CE0095DFA7 /* RequiredRule.swift */; };
62DC8D6E1AAA42CE0095DFA7 /* PasswordRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DC8D6B1AAA42CE0095DFA7 /* PasswordRule.swift */; };
62DC8D711AAA43110095DFA7 /* ZipCodeRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DC8D701AAA43110095DFA7 /* ZipCodeRule.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -41,6 +37,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
628637251AAA474B00BC8FCF /* MinLengthRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinLengthRule.swift; sourceTree = "<group>"; };
628637271AAA49E300BC8FCF /* ConfirmRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfirmRule.swift; sourceTree = "<group>"; };
62D1AE171A1E6D4400E4DFF8 /* Validator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Validator.app; sourceTree = BUILT_PRODUCTS_DIR; };
62D1AE1B1A1E6D4400E4DFF8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
62D1AE1C1A1E6D4400E4DFF8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@ -51,21 +49,15 @@
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>"; };
62DC8D641AAA42520095DFA7 /* Rule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rule.swift; sourceTree = "<group>"; };
62DC8D671AAA42920095DFA7 /* FullNameRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullNameRule.swift; sourceTree = "<group>"; };
62DC8D691AAA42CE0095DFA7 /* EmailRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailRule.swift; sourceTree = "<group>"; };
62DC8D6A1AAA42CE0095DFA7 /* RequiredRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequiredRule.swift; sourceTree = "<group>"; };
62DC8D6B1AAA42CE0095DFA7 /* PasswordRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordRule.swift; sourceTree = "<group>"; };
62DC8D701AAA43110095DFA7 /* ZipCodeRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipCodeRule.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -89,8 +81,8 @@
626BE5EE1A9B8EC600FE6D5C /* Swift-Validator */ = {
isa = PBXGroup;
children = (
62DC8D661AAA42700095DFA7 /* Rules */,
62D1AE3C1A1E6FAF00E4DFF8 /* Core */,
62D1AE5B1A1E701B00E4DFF8 /* Validations */,
);
name = "Swift-Validator";
sourceTree = "<group>";
@ -155,30 +147,26 @@
62D1AE3C1A1E6FAF00E4DFF8 /* Core */ = {
isa = PBXGroup;
children = (
62D1AE531A1E700200E4DFF8 /* ValidationRuleType.swift */,
62D1AE541A1E700200E4DFF8 /* Validator.swift */,
62D1AE451A1E6FF800E4DFF8 /* ValidationError.swift */,
62D1AE461A1E6FF800E4DFF8 /* ValidationErrorType.swift */,
62D1AE471A1E6FF800E4DFF8 /* ValidationFactory.swift */,
62D1AE481A1E6FF800E4DFF8 /* ValidationRule.swift */,
62D1AE441A1E6FF800E4DFF8 /* Validation.swift */,
);
name = Core;
sourceTree = "<group>";
};
62D1AE5B1A1E701B00E4DFF8 /* Validations */ = {
62DC8D661AAA42700095DFA7 /* Rules */ = {
isa = PBXGroup;
children = (
62D1AE3D1A1E6FEF00E4DFF8 /* FullNameValidation.swift */,
62D1AE421A1E6FF800E4DFF8 /* PhoneNumberValidation.swift */,
62D1AE431A1E6FF800E4DFF8 /* RequiredValidation.swift */,
62D1AE3F1A1E6FF800E4DFF8 /* EmailValidation.swift */,
62D1AE411A1E6FF800E4DFF8 /* MinLengthValidation.swift */,
62D1AE401A1E6FF800E4DFF8 /* MaxLengthValidation.swift */,
62D1AE561A1E700200E4DFF8 /* PasswordValidation.swift */,
62D1AE551A1E700200E4DFF8 /* ZipCodeValidation.swift */,
628637251AAA474B00BC8FCF /* MinLengthRule.swift */,
62DC8D641AAA42520095DFA7 /* Rule.swift */,
62DC8D691AAA42CE0095DFA7 /* EmailRule.swift */,
62DC8D6A1AAA42CE0095DFA7 /* RequiredRule.swift */,
62DC8D6B1AAA42CE0095DFA7 /* PasswordRule.swift */,
62DC8D671AAA42920095DFA7 /* FullNameRule.swift */,
62DC8D701AAA43110095DFA7 /* ZipCodeRule.swift */,
628637271AAA49E300BC8FCF /* ConfirmRule.swift */,
);
name = Validations;
name = Rules;
sourceTree = "<group>";
};
/* End PBXGroup section */
@ -281,23 +269,19 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
62D1AE4C1A1E6FF800E4DFF8 /* PhoneNumberValidation.swift in Sources */,
62D1AE5A1A1E700200E4DFF8 /* PasswordValidation.swift in Sources */,
62DC8D681AAA42920095DFA7 /* FullNameRule.swift in Sources */,
62D1AE4F1A1E6FF800E4DFF8 /* ValidationError.swift in Sources */,
62D1AE3E1A1E6FEF00E4DFF8 /* FullNameValidation.swift in Sources */,
62D1AE4B1A1E6FF800E4DFF8 /* MinLengthValidation.swift in Sources */,
62DC8D6E1AAA42CE0095DFA7 /* PasswordRule.swift in Sources */,
62DC8D6C1AAA42CE0095DFA7 /* EmailRule.swift in Sources */,
628637281AAA49E300BC8FCF /* ConfirmRule.swift in Sources */,
62DC8D651AAA42520095DFA7 /* Rule.swift in Sources */,
62D1AE1F1A1E6D4400E4DFF8 /* ViewController.swift in Sources */,
62D1AE4E1A1E6FF800E4DFF8 /* Validation.swift in Sources */,
62DC8D6D1AAA42CE0095DFA7 /* RequiredRule.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 */,
628637261AAA474B00BC8FCF /* MinLengthRule.swift in Sources */,
62DC8D711AAA43110095DFA7 /* ZipCodeRule.swift in Sources */,
62D1AE521A1E6FF800E4DFF8 /* ValidationRule.swift in Sources */,
62D1AE4A1A1E6FF800E4DFF8 /* MaxLengthValidation.swift in Sources */,
62D1AE4D1A1E6FF800E4DFF8 /* RequiredValidation.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6250" systemVersion="14A389" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6244"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<scenes>
@ -23,11 +24,6 @@
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Darth Vader" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="3uK-Qz-rLB">
<rect key="frame" x="0.0" y="-30" width="97" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Email" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MOa-Y4-nDo">
<rect key="frame" x="0.0" y="-21" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
@ -39,7 +35,7 @@
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Phone Number" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gjm-Ww-AYk">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Phone" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gjm-Ww-AYk">
<rect key="frame" x="0.0" y="-21" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
@ -133,6 +129,45 @@
</mask>
</variation>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Email Confirm" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bLY-f8-5zf">
<rect key="frame" x="0.0" y="-21" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="darth@deathstar.com" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="O9u-O8-mPB">
<rect key="frame" x="0.0" y="-30" width="97" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Required" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="K92-ww-iP3">
<rect key="frame" x="0.0" y="-21" width="42" height="21"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="227" id="SdK-o6-MXe"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="21" id="bi4-dv-zcg"/>
<constraint firstAttribute="height" constant="21" id="fjW-er-ghM"/>
<constraint firstAttribute="width" constant="227" id="hpm-mc-xeq"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="default">
<mask key="constraints">
<exclude reference="fjW-er-ghM"/>
<exclude reference="hpm-mc-xeq"/>
<exclude reference="bi4-dv-zcg"/>
<exclude reference="SdK-o6-MXe"/>
</mask>
</variation>
<variation key="heightClass=regular-widthClass=compact">
<mask key="constraints">
<include reference="fjW-er-ghM"/>
<include reference="hpm-mc-xeq"/>
<include reference="bi4-dv-zcg"/>
<include reference="SdK-o6-MXe"/>
</mask>
</variation>
</label>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Required" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pon-eT-yrd">
<rect key="frame" x="0.0" y="-21" width="42" height="21"/>
<constraints>
@ -170,6 +205,11 @@
<action selector="submitTapped:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="wmP-d4-6hM"/>
</connections>
</button>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Darth Vader" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="3uK-Qz-rLB">
<rect key="frame" x="0.0" y="-30" width="97" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
</textField>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
@ -189,16 +229,17 @@
<variation key="heightClass=regular-widthClass=compact" constant="20"/>
</constraint>
<constraint firstItem="Zu1-QS-1Yk" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="B28-e9-frX"/>
<constraint firstItem="O9u-O8-mPB" firstAttribute="top" secondItem="bLY-f8-5zf" secondAttribute="bottom" constant="8" id="DtP-3P-vpf"/>
<constraint firstItem="Zu1-QS-1Yk" firstAttribute="trailing" secondItem="kh9-bI-dsS" secondAttribute="trailingMargin" id="Egg-Ya-137"/>
<constraint firstItem="ibf-gq-p2X" firstAttribute="trailing" secondItem="kh9-bI-dsS" secondAttribute="trailingMargin" id="H9S-E2-xEW"/>
<constraint firstItem="3uK-Qz-rLB" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="Ion-9h-zfc"/>
<constraint firstItem="qLN-YJ-gUx" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="K5d-es-d8h"/>
<constraint firstItem="K92-ww-iP3" firstAttribute="top" secondItem="ibf-gq-p2X" secondAttribute="bottom" constant="25" id="LFO-jd-axU"/>
<constraint firstItem="Gw5-ME-8qn" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="M9f-Bp-Gbg"/>
<constraint firstItem="Gjm-Ww-AYk" firstAttribute="top" secondItem="ibf-gq-p2X" secondAttribute="bottom" constant="18" id="Moy-q7-oUR">
<variation key="heightClass=regular-widthClass=compact" constant="20"/>
</constraint>
<constraint firstItem="5Yi-nP-Hf3" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="8" id="OdB-kn-E8t"/>
<constraint firstItem="Gjm-Ww-AYk" firstAttribute="top" secondItem="O9u-O8-mPB" secondAttribute="bottom" constant="21" id="PEI-qV-ay9"/>
<constraint firstItem="2or-KE-2NU" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="PaQ-6i-9mo"/>
<constraint firstItem="O9u-O8-mPB" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="T1M-HW-h60"/>
<constraint firstItem="3uK-Qz-rLB" firstAttribute="top" secondItem="5Yi-nP-Hf3" secondAttribute="bottom" constant="8" id="VDP-DY-jUh"/>
<constraint firstItem="qLN-YJ-gUx" firstAttribute="top" secondItem="F6l-Qg-562" secondAttribute="bottom" constant="8" id="VDe-WO-sJU"/>
<constraint firstItem="ibf-gq-p2X" firstAttribute="top" secondItem="MOa-Y4-nDo" secondAttribute="bottom" constant="9" id="VWf-RY-YhF">
@ -209,6 +250,7 @@
<constraint firstItem="khR-OB-bgx" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="bud-iX-j0f"/>
<constraint firstItem="Gjm-Ww-AYk" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="cNA-Yf-qTv"/>
<constraint firstItem="qLN-YJ-gUx" firstAttribute="top" secondItem="Gjm-Ww-AYk" secondAttribute="bottom" constant="8" id="d08-t5-VT0"/>
<constraint firstItem="O9u-O8-mPB" firstAttribute="trailing" secondItem="kh9-bI-dsS" secondAttribute="trailingMargin" id="ds9-gC-21F"/>
<constraint firstItem="Zu1-QS-1Yk" firstAttribute="top" secondItem="2or-KE-2NU" secondAttribute="bottom" constant="9" id="eAd-Wu-obj">
<variation key="heightClass=regular-widthClass=compact" constant="8"/>
</constraint>
@ -217,19 +259,19 @@
<variation key="heightClass=regular-widthClass=compact" constant="20"/>
</constraint>
<constraint firstItem="v4N-fz-1u1" firstAttribute="trailing" secondItem="kh9-bI-dsS" secondAttribute="trailingMargin" id="hpD-Om-vt4"/>
<constraint firstItem="O9u-O8-mPB" firstAttribute="top" secondItem="K92-ww-iP3" secondAttribute="bottom" constant="8" id="htl-Vj-x3w"/>
<constraint firstItem="Zu1-QS-1Yk" firstAttribute="top" secondItem="pon-eT-yrd" secondAttribute="bottom" constant="9" id="k2e-YN-zp4">
<variation key="heightClass=regular-widthClass=compact" constant="8"/>
</constraint>
<constraint firstItem="F6l-Qg-562" firstAttribute="top" secondItem="ibf-gq-p2X" secondAttribute="bottom" constant="18" id="oYt-uZ-XMO">
<variation key="heightClass=regular-widthClass=compact" constant="20"/>
</constraint>
<constraint firstItem="bLY-f8-5zf" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="qWu-f4-OGv"/>
<constraint firstItem="3uK-Qz-rLB" firstAttribute="top" secondItem="khR-OB-bgx" secondAttribute="bottom" constant="8" id="rRl-bj-wIm"/>
<constraint firstItem="bLY-f8-5zf" firstAttribute="top" secondItem="ibf-gq-p2X" secondAttribute="bottom" constant="25" id="sh3-oU-dEE"/>
<constraint firstItem="3uK-Qz-rLB" firstAttribute="trailing" secondItem="kh9-bI-dsS" secondAttribute="trailingMargin" id="t8X-0Q-RPA"/>
<constraint firstItem="K92-ww-iP3" firstAttribute="trailing" secondItem="kh9-bI-dsS" secondAttribute="trailingMargin" id="wny-JX-bf3"/>
</constraints>
<variation key="default">
<mask key="subviews">
<exclude reference="khR-OB-bgx"/>
<exclude reference="3uK-Qz-rLB"/>
<exclude reference="MOa-Y4-nDo"/>
<exclude reference="ibf-gq-p2X"/>
<exclude reference="Gjm-Ww-AYk"/>
@ -239,8 +281,12 @@
<exclude reference="5Yi-nP-Hf3"/>
<exclude reference="v4N-fz-1u1"/>
<exclude reference="F6l-Qg-562"/>
<exclude reference="bLY-f8-5zf"/>
<exclude reference="O9u-O8-mPB"/>
<exclude reference="K92-ww-iP3"/>
<exclude reference="pon-eT-yrd"/>
<exclude reference="Gw5-ME-8qn"/>
<exclude reference="3uK-Qz-rLB"/>
</mask>
<mask key="constraints">
<exclude reference="bud-iX-j0f"/>
@ -259,16 +305,23 @@
<exclude reference="aeN-FP-LX6"/>
<exclude reference="55u-lk-Vl8"/>
<exclude reference="OdB-kn-E8t"/>
<exclude reference="Moy-q7-oUR"/>
<exclude reference="cNA-Yf-qTv"/>
<exclude reference="7J7-45-P5G"/>
<exclude reference="hpD-Om-vt4"/>
<exclude reference="qWu-f4-OGv"/>
<exclude reference="sh3-oU-dEE"/>
<exclude reference="DtP-3P-vpf"/>
<exclude reference="T1M-HW-h60"/>
<exclude reference="ds9-gC-21F"/>
<exclude reference="htl-Vj-x3w"/>
<exclude reference="LFO-jd-axU"/>
<exclude reference="wny-JX-bf3"/>
<exclude reference="PEI-qV-ay9"/>
<exclude reference="cNA-Yf-qTv"/>
<exclude reference="60N-Dv-oSw"/>
<exclude reference="K5d-es-d8h"/>
<exclude reference="VDe-WO-sJU"/>
<exclude reference="d08-t5-VT0"/>
<exclude reference="7Ce-Df-SWx"/>
<exclude reference="oYt-uZ-XMO"/>
<exclude reference="A7P-r9-8FC"/>
<exclude reference="PaQ-6i-9mo"/>
<exclude reference="B28-e9-frX"/>
@ -284,7 +337,6 @@
<variation key="heightClass=regular-widthClass=compact">
<mask key="subviews">
<include reference="khR-OB-bgx"/>
<include reference="3uK-Qz-rLB"/>
<include reference="MOa-Y4-nDo"/>
<include reference="ibf-gq-p2X"/>
<include reference="Gjm-Ww-AYk"/>
@ -294,8 +346,12 @@
<include reference="5Yi-nP-Hf3"/>
<include reference="v4N-fz-1u1"/>
<include reference="F6l-Qg-562"/>
<include reference="bLY-f8-5zf"/>
<include reference="O9u-O8-mPB"/>
<include reference="K92-ww-iP3"/>
<include reference="pon-eT-yrd"/>
<include reference="Gw5-ME-8qn"/>
<include reference="3uK-Qz-rLB"/>
</mask>
<mask key="constraints">
<include reference="bud-iX-j0f"/>
@ -314,16 +370,23 @@
<include reference="aeN-FP-LX6"/>
<include reference="55u-lk-Vl8"/>
<include reference="OdB-kn-E8t"/>
<include reference="Moy-q7-oUR"/>
<include reference="cNA-Yf-qTv"/>
<include reference="7J7-45-P5G"/>
<include reference="hpD-Om-vt4"/>
<include reference="qWu-f4-OGv"/>
<include reference="sh3-oU-dEE"/>
<include reference="DtP-3P-vpf"/>
<include reference="T1M-HW-h60"/>
<include reference="ds9-gC-21F"/>
<include reference="htl-Vj-x3w"/>
<include reference="LFO-jd-axU"/>
<include reference="wny-JX-bf3"/>
<include reference="PEI-qV-ay9"/>
<include reference="cNA-Yf-qTv"/>
<include reference="60N-Dv-oSw"/>
<include reference="K5d-es-d8h"/>
<include reference="VDe-WO-sJU"/>
<include reference="d08-t5-VT0"/>
<include reference="7Ce-Df-SWx"/>
<include reference="oYt-uZ-XMO"/>
<include reference="A7P-r9-8FC"/>
<include reference="PaQ-6i-9mo"/>
<include reference="B28-e9-frX"/>
@ -338,6 +401,8 @@
</variation>
</view>
<connections>
<outlet property="emailConfirmErrorLabel" destination="K92-ww-iP3" id="EAZ-PN-mPh"/>
<outlet property="emailConfirmTextField" destination="O9u-O8-mPB" id="gsp-WL-cSi"/>
<outlet property="emailErrorLabel" destination="v4N-fz-1u1" id="aOl-vh-2bv"/>
<outlet property="emailTextField" destination="ibf-gq-p2X" id="th5-OF-xXo"/>
<outlet property="fullNameErrorLabel" destination="5Yi-nP-Hf3" id="OUq-Yl-ryr"/>

View File

@ -0,0 +1,32 @@
//
// ConfirmRule.swift
// Validator
//
// Created by Jeff Potter on 3/6/15.
// Copyright (c) 2015 jpotts18. All rights reserved.
//
import Foundation
import UIKit
class ConfirmationRule: Rule {
let confirmField: UITextField
init(confirmField: UITextField) {
self.confirmField = confirmField
}
func validate(value: String) -> Bool {
if self.confirmField.text == value {
return true
} else {
return false
}
}
func errorMessage() -> String {
return "This field does not match"
}
}

40
Validator/EmailRule.swift Normal file
View File

@ -0,0 +1,40 @@
//
// EmailValidation.swift
// Pingo
//
// Created by Jeff Potter on 11/11/14.
// Copyright (c) 2014 Byron Mackay. All rights reserved.
//
import Foundation
class EmailRule: Rule {
let REGEX = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}"
init(){}
init(regex:String){
self.REGEX = regex
}
var message:String {
return "Must be a valid email address"
}
func validate(value:String) -> Bool {
if let emailTest = NSPredicate(format: "SELF MATCHES %@", REGEX) {
if emailTest.evaluateWithObject(value) {
return true
} else {
return false
}
}
return false
}
func errorMessage() -> String {
return self.message
}
}

View File

@ -1,26 +0,0 @@
//
// 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,30 @@
//
// FullNameValidation.swift
// Pingo
//
// Created by Jeff Potter on 11/19/14.
// Copyright (c) 2014 Byron Mackay. All rights reserved.
//
import Foundation
class FullNameRule : Rule {
var message:String {
return "Please provide a first & last name"
}
func validate(value:String) -> Bool {
var nameArray:[String] = split(value) { $0 == " " }
if nameArray.count >= 2 {
return true
}
return false
}
func errorMessage() -> String {
return message
}
}

View File

@ -1,22 +0,0 @@
//
// 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

@ -1,20 +0,0 @@
//
// 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,32 @@
//
// LengthRule.swift
// Validator
//
// Created by Jeff Potter on 3/6/15.
// Copyright (c) 2015 jpotts18. All rights reserved.
//
import Foundation
class MinLengthRule : Rule {
let DEFAULT_MIN_LENGTH:Int = 3
init(){}
init(length:Int){
self.DEFAULT_MIN_LENGTH = length
}
func errorMessage() -> String {
return "Must be at least \(DEFAULT_MIN_LENGTH) characters long"
}
func validate(value: String) -> Bool {
if countElements(value) <= DEFAULT_MIN_LENGTH {
return false
}
return true
}
}

View File

@ -1,20 +0,0 @@
//
// 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

@ -8,7 +8,7 @@
import Foundation
class PasswordValidation : Validation {
class PasswordRule : Rule {
// Alternative Regexes
@ -19,15 +19,30 @@ class PasswordValidation : Validation {
// var PASSWORD_REGEX = "^(?=.*?[A-Z])(?=.*?[0-9])(?=.*?[a-z]).*?$"
// 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) {
var REGEX = "^(?=.*?[A-Z]).{8,}$"
init(){}
init(regex:String){
self.REGEX = regex
}
var message:String {
return "Must be 8 characters with 1 uppercase"
}
func validate(value: String) -> Bool {
if let passwordTes = NSPredicate(format: "SELF MATCHES %@", REGEX) {
if passwordTes.evaluateWithObject(value) {
return (true, .NoError)
return true
}
return (false, .Password)
return false
}
return (false, .Password)
return false
}
func errorMessage() -> String {
return self.message
}
}

View File

@ -0,0 +1,33 @@
//
// PhoneValidation.swift
// Pingo
//
// Created by Jeff Potter on 11/11/14.
// Copyright (c) 2014 Byron Mackay. All rights reserved.
//
import Foundation
class PhoneNumberRule: Rule {
// let PHONE_REGEX = "^\\d{3}-\\d{3}-\\d{4}$"
let PHONE_REGEX = "^\\d{10}$"
var message:String {
return "Enter a valid 10 digit phone number"
}
func validate(value: String) -> Bool {
if let phoneTest = NSPredicate(format: "SELF MATCHES %@", PHONE_REGEX) {
if phoneTest.evaluateWithObject(value) {
return true
}
return false
}
return false
}
func errorMessage() -> String {
return message
}
}

View File

@ -1,24 +0,0 @@
//
// 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,30 @@
//
// Required.swift
// pyur-ios
//
// Created by Jeff Potter on 12/22/14.
// Copyright (c) 2014 ringseven. All rights reserved.
//
import Foundation
class RequiredRule: Rule {
init(){}
var message: String {
return "This field is required"
}
func validate(value:String) -> Bool {
if value.isEmpty {
return false
}
return true
}
func errorMessage() -> String {
return message
}
}

View File

@ -1,19 +0,0 @@
//
// 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

@ -8,6 +8,7 @@
import Foundation
protocol Validation {
func validate(value:String) -> (Bool, ValidationErrorType)
}
protocol Rule {
func validate(value:String) -> Bool
func errorMessage() -> String
}

View File

@ -11,11 +11,12 @@ import UIKit
class ValidationError {
let textField:UITextField
let error:ValidationErrorType
var errorLabel:UILabel?
let errorMessage:String
init(textField:UITextField, error:ValidationErrorType){
init(textField:UITextField, error:String){
self.textField = textField
self.error = error
self.errorMessage = error
}
}

View File

@ -1,43 +0,0 @@
//
// 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

@ -1,32 +0,0 @@
//
// 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

@ -10,20 +10,21 @@ import Foundation
import UIKit
class ValidationRule {
let textField:UITextField
var rules:[ValidationRuleType] = []
var textField:UITextField
var errorLabel:UILabel?
var rules:[Rule] = []
init(textField:UITextField, rules:[ValidationRuleType]){
init(textField: UITextField, rules:[Rule], errorLabel:UILabel?){
self.textField = textField
self.errorLabel = errorLabel
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)
var isValid:Bool = rule.validate(textField.text)
if !isValid {
return ValidationError(textField: self.textField, error: rule.errorMessage())
}
}
return nil

View File

@ -1,20 +0,0 @@
//
// 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
}

View File

@ -9,57 +9,52 @@
import Foundation
import UIKit
protocol ValidationDelegate {
@objc protocol ValidationDelegate {
func validationWasSuccessful()
func validationFailed(errors:[String:ValidationError])
}
protocol ValidationFieldDelegate {
func validationFieldFailed(key:String, error:ValidationError)
func validationFieldSuccess(key:String, validField:UITextField)
func validationFailed(errors:[UITextField:ValidationError])
}
class Validator {
// dictionary to handle complex view hierarchies like dynamic tableview cells
var validationRules:[String:ValidationRule] = [:]
var validationErrors:[String:ValidationError] = [:]
var errors:[UITextField:ValidationError] = [:]
var validations:[UITextField:ValidationRule] = [:]
init(){}
// MARK: Using Keys
func registerFieldByKey(key:String, textField:UITextField, rules:[ValidationRuleType]) {
validationRules[key] = ValidationRule(textField: textField, rules: rules)
func registerField(textField:UITextField, rules:[Rule]) {
validations[textField] = ValidationRule(textField: textField, rules: rules, errorLabel: nil)
}
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 registerField(textField:UITextField, errorLabel:UILabel, rules:[Rule]) {
validations[textField] = ValidationRule(textField: textField, rules:rules, errorLabel:errorLabel)
}
func validateAllKeys(delegate:ValidationDelegate){
func validateAll(delegate:ValidationDelegate) {
for key in validationRules.keys {
if let currentRule:ValidationRule = validationRules[key] {
for field in validations.keys {
if let currentRule:ValidationRule = validations[field] {
if var error:ValidationError = currentRule.validateField() {
validationErrors[key] = error
if (currentRule.errorLabel != nil) {
error.errorLabel = currentRule.errorLabel
}
errors[field] = error
} else {
validationErrors.removeValueForKey(key)
errors.removeValueForKey(field)
}
}
}
if validationErrors.isEmpty {
if errors.isEmpty {
delegate.validationWasSuccessful()
} else {
delegate.validationFailed(validationErrors)
delegate.validationFailed(errors)
}
}
func clearErrors(){
self.errors = [:]
}
}

View File

@ -6,8 +6,10 @@
// Copyright (c) 2014 jpotts18. All rights reserved.
//
import Foundation
import UIKit
class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate {
// TextFields
@ -15,29 +17,26 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate
@IBOutlet weak var emailTextField: UITextField!
@IBOutlet weak var phoneNumberTextField: UITextField!
@IBOutlet weak var zipcodeTextField: UITextField!
@IBOutlet weak var emailConfirmTextField: UITextField!
// Error Labels
@IBOutlet weak var fullNameErrorLabel: UILabel!
@IBOutlet weak var emailErrorLabel: UILabel!
@IBOutlet weak var phoneNumberErrorLabel: UILabel!
@IBOutlet weak var zipcodeErrorLabel: UILabel!
let KEYS = ["Full Name", "Email", "Phone", "ZipCode"]
@IBOutlet weak var emailConfirmErrorLabel: UILabel!
let validator = Validator()
override func viewDidLoad() {
super.viewDidLoad()
fullNameTextField.delegate = self
emailTextField.delegate = self
phoneNumberTextField.delegate = self
zipcodeTextField.delegate = self
validator.registerFieldByKey(KEYS[0], textField: fullNameTextField, rules: [.Required, .FullName])
validator.registerFieldByKey(KEYS[1], textField: emailTextField, rules: [.Required, .Email])
validator.registerFieldByKey(KEYS[2], textField: phoneNumberTextField, rules: [.Required, .PhoneNumber])
validator.registerFieldByKey(KEYS[3], textField: zipcodeTextField, rules: [.Required, .ZipCode])
validator.registerField(fullNameTextField, errorLabel: fullNameErrorLabel , rules: [RequiredRule(), FullNameRule()])
validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule()])
validator.registerField(emailConfirmTextField, errorLabel: emailConfirmErrorLabel, rules: [RequiredRule(), EmailRule(), ConfirmationRule(confirmField: emailTextField)])
validator.registerField(phoneNumberTextField, errorLabel: phoneNumberErrorLabel, rules: [RequiredRule(), MinLengthRule(length: 9)])
validator.registerField(zipcodeTextField, errorLabel: zipcodeErrorLabel, rules: [RequiredRule(), ZipCodeRule()])
}
@ -49,17 +48,11 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate
@IBAction func submitTapped(sender: AnyObject) {
println("Validating...")
validator.validateAllKeys(self)
self.clearErrors()
validator.validateAll(self)
}
// MARK: Error Styling
func setError(label:UILabel, error:ValidationError) {
label.hidden = false
label.text = error.error.description()
error.textField.layer.borderColor = UIColor.redColor().CGColor
error.textField.layer.borderWidth = 2.0
}
func removeError(label:UILabel, textField:UITextField) {
label.hidden = true
@ -75,45 +68,33 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate
// MARK: ValidationDelegate Methods
func validationFailed(errors: [String : ValidationError]) {
println("Found \(errors.count) errors")
if var fullNameError = errors[KEYS[0]] {
setError(fullNameErrorLabel, error: fullNameError)
} else {
removeError(fullNameErrorLabel, textField: fullNameTextField)
}
if var emailNameError = errors[KEYS[1]] {
setError(emailErrorLabel, error: emailNameError)
} else {
removeError(emailErrorLabel, textField: emailTextField)
}
if var phoneError = errors[KEYS[2]] {
setError(phoneNumberErrorLabel, error: phoneError)
} else {
removeError(phoneNumberErrorLabel, textField: phoneNumberTextField)
}
if var zipError = errors[KEYS[3]] {
setError(zipcodeErrorLabel, error: zipError)
} else {
removeError(zipcodeErrorLabel, textField: zipcodeTextField)
func validationWasSuccessful() {
println("Validation Success!")
var alert = UIAlertController(title: "Success", message: "You are validated!", preferredStyle: UIAlertControllerStyle.Alert)
var defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alert.addAction(defaultAction)
self.presentViewController(alert, animated: true, completion: nil)
}
func validationFailed(errors:[UITextField:ValidationError]) {
println("Validation FAILED!")
self.setErrors()
}
private func setErrors(){
for (field, error) in validator.errors {
field.layer.borderColor = UIColor.redColor().CGColor
field.layer.borderWidth = 1.0
error.errorLabel?.text = error.errorMessage
error.errorLabel?.hidden = false
}
}
func validationWasSuccessful() {
println("Everything checks out!")
removeAllErrors()
var alert = UIAlertController(title: "Valid!", message: "Everything looks good to me", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
private func clearErrors(){
for (field, error) in validator.errors {
field.layer.borderWidth = 0.0
error.errorLabel?.hidden = true
}
}
}

View File

@ -0,0 +1,38 @@
//
// ZipCodeRule.swift
// Validator
//
// Created by Jeff Potter on 3/6/15.
// Copyright (c) 2015 jpotts18. All rights reserved.
//
import Foundation
class ZipCodeRule: Rule {
let REGEX = "\\d{5}"
init(){}
init(regex:String){
self.REGEX = regex
}
var message: String {
return "Enter a valid 5 digit zipcode"
}
func validate(value: String) -> Bool {
if let zipTest = NSPredicate(format: "SELF MATCHES %@", REGEX) {
if zipTest.evaluateWithObject(value) {
return true
}
return false
}
return false
}
func errorMessage() -> String {
return message
}
}

View File

@ -1,24 +0,0 @@
//
// 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)
}
}