From 7c6bf284c47786a4157fc4c99942d20e1aec3b57 Mon Sep 17 00:00:00 2001 From: David Patterson Date: Tue, 1 Mar 2016 09:58:14 -0600 Subject: [PATCH 1/3] updated podspec to 3.0.4 --- SwiftValidator.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SwiftValidator.podspec b/SwiftValidator.podspec index 4e31e32..add6793 100644 --- a/SwiftValidator.podspec +++ b/SwiftValidator.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SwiftValidator" - s.version = "3.0.1" + s.version = "3.0.4" s.summary = "A UITextField Validation library for Swift" s.homepage = "https://github.com/jpotts18/SwiftValidator" s.screenshots = "https://raw.githubusercontent.com/jpotts18/SwiftValidator/master/swift-validator-v2.gif" @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.social_media_url = "http://twitter.com/jpotts18" s.platform = :ios s.ios.deployment_target = '8.0' - s.source = { :git => "https://github.com/jpotts18/SwiftValidator.git", :tag => "3.0.1" } + s.source = { :git => "https://github.com/jpotts18/SwiftValidator.git", :tag => "3.0.4" } s.source_files = "SwiftValidator/**/*.swift" s.exclude_files = "Validator/AppDelegate.swift" s.frameworks = ['Foundation', 'UIKit'] From b57e924b26862c11c34d4eaf4bd2d8c9e33355c4 Mon Sep 17 00:00:00 2001 From: David Patterson Date: Tue, 1 Mar 2016 12:47:58 -0600 Subject: [PATCH 2/3] beta version of remote validation --- SwiftValidator/Core/ValidationDelegate.swift | 14 ++- SwiftValidator/Core/Validator.swift | 93 +++++++++++++++----- SwiftValidator/Rules/ValidationRule.swift | 13 ++- Validator/ViewController.swift | 30 ++++++- 4 files changed, 118 insertions(+), 32 deletions(-) diff --git a/SwiftValidator/Core/ValidationDelegate.swift b/SwiftValidator/Core/ValidationDelegate.swift index 79ffe7d..49471dc 100644 --- a/SwiftValidator/Core/ValidationDelegate.swift +++ b/SwiftValidator/Core/ValidationDelegate.swift @@ -13,15 +13,21 @@ import UIKit */ @objc public protocol ValidationDelegate { /** - This method will be called on delegate object when validation is successful. + This delegate method will be called on delegate object when validation is successful. - returns: No return value. */ func validationSuccessful() /** - This method will be called on delegate object when validation fails. + This delegae method will be called on delegate object when validation fails. - returns: No return value. */ func validationFailed(errors: [UITextField:ValidationError]) - - optional func remoteRequest(text: String, urlString: String, completion:(result: Bool) -> Void) + /** + This delegate method is called on fields that require remote validation. + - parameter text: String to is sent to server to be validated. + - parameter urlString: String of url endpoint that will be used to validate text. + - parameter completion: closure that holds the result of the server validation request. Should be set to true if server validation was a success. Should return false if server validation failed. + - returns: No return value. + */ + optional func remoteValidationRequest(text: String, urlString: String, completion:(result: Bool) -> Void) } diff --git a/SwiftValidator/Core/Validator.swift b/SwiftValidator/Core/Validator.swift index 271885e..7b4123a 100644 --- a/SwiftValidator/Core/Validator.swift +++ b/SwiftValidator/Core/Validator.swift @@ -54,12 +54,18 @@ public class Validator { } } } - /// Validate all fields with completion handler + /** + This method is used to validate all fields registered to Validator. If validation is unsuccessful, + field gets added to errors dictionary. Completion closure is used to validator know when all fields + have undergone validation attempt. + - parameter completion: Bool that is set to true when all fields have experienced validation attempt. + - returns: No return value. + */ private func validateAllFields(completion: (finished: Bool) -> Void) { errors = [:] for (textField, rule) in validations { - if rule.remoteURLString != nil { + if rule.remoteInfo != nil { validateRemoteField(textField, completion: { status -> Void in self.completedValidationsCount = self.completedValidationsCount + 1 if self.completedValidationsCount == self.validations.count { @@ -77,40 +83,66 @@ public class Validator { } } } - /// Validate remote field + /** + This method is used to validate a field that will need to also be validated via remote request. + - parameter textField: TextField of field that is being validated. + - parameter completion: Closure that holds the status of textField's validation. Is set to true + after remote validation has ended, regardless of whether the validation was a success or failure. + - returns: No return value. + */ private func validateRemoteField(textField: UITextField, completion: (status: Bool) -> Void) { if let fieldRule = validations[textField] { - delegate!.remoteRequest!(textField.text!, urlString: fieldRule.remoteURLString!, completion: { result -> Void in - if result { - // Carry on with validation as remote validation passed - self.validateRegularField(fieldRule.textField) - } else { - // Stop validation because remote validation failed - // Validation Failed on remote call - let error = ValidationError(textField: fieldRule.textField, errorLabel: UILabel(), error: "Field already taken") - self.errors[fieldRule.textField] = error - if let transform = self.errorStyleTransform { - transform(validationError: error) + // Carry on with validation as remote validation passed + if self.validateRegularField(fieldRule.textField) { + delegate!.remoteValidationRequest!(textField.text!, urlString: fieldRule.remoteInfo!.urlString, completion: { result -> Void in + if result { + // Carry on with validation as remote validation passed + //self.validateRegularField(fieldRule.textField) + if let transform = self.successStyleTransform { + transform(validationRule: fieldRule) + } + } else { + // Stop validation because remote validation failed + // Validation Failed on remote call + let error = ValidationError(textField: fieldRule.textField, errorLabel: fieldRule.errorLabel, error: fieldRule.remoteInfo!.error) + self.errors[fieldRule.textField] = error + if let transform = self.errorStyleTransform { + transform(validationError: error) + } } - } + completion(status: true) + }) + } else { + // Fail validation completion(status: true) - }) + } + } } - /// Validate regular field (non-remote) - private func validateRegularField(textField: UITextField) { + /** + Method used to validate a regular field (non-remote). + - parameter: TextField of field that is undergoing validation + - returns: A Bool that represents whether the validation was a success or failure, returns true for the + former and false for the latter. + */ + private func validateRegularField(textField: UITextField) -> Bool { if let fieldRule = validations[textField] { if let error = fieldRule.validateField() { errors[textField] = error if let transform = self.errorStyleTransform { transform(validationError: error) + return false } } else { if let transform = self.successStyleTransform { - transform(validationRule: fieldRule) + if fieldRule.remoteInfo == nil { + transform(validationRule: fieldRule) + } + return true } } } + return false } // MARK: Public functions @@ -161,8 +193,8 @@ public class Validator { - parameter Rule: An array which holds different rules to validate against textField. - returns: No return value */ - public func registerField(textField:UITextField, rules:[Rule], remoteURLString: String? = nil) { - validations[textField] = ValidationRule(textField: textField, rules: rules, errorLabel: nil, remoteURLString: remoteURLString) + public func registerField(textField:UITextField, rules:[Rule], remoteInfo: (String, String)? = nil) { + validations[textField] = ValidationRule(textField: textField, rules: rules, errorLabel: nil, remoteInfo: remoteInfo) } /** @@ -173,8 +205,9 @@ public class Validator { - parameter rules: A Rule array that holds different rules that apply to said textField. - returns: No return value */ - public func registerField(textField:UITextField, errorLabel:UILabel, rules:[Rule]) { - validations[textField] = ValidationRule(textField: textField, rules:rules, errorLabel:errorLabel) + + public func registerField(textField:UITextField, errorLabel:UILabel, rules:[Rule], remoteInfo: (String, String)? = nil) { + validations[textField] = ValidationRule(textField: textField, rules:rules, errorLabel:errorLabel, remoteInfo: remoteInfo) } /** @@ -205,6 +238,20 @@ public class Validator { } + public func validate() { + self.validateAllFields { finished -> Void in + if self.errors.isEmpty { + // call success method if it's implemented + self.delegate!.validationSuccessful() + } else { + // call failure method if it's implemented + self.delegate!.validationFailed(self.errors) + } + // set number of completed validations back to 0 + self.completedValidationsCount = 0 + } + } + /** This method validates all fields in validator and sets any errors to errors parameter of callback. diff --git a/SwiftValidator/Rules/ValidationRule.swift b/SwiftValidator/Rules/ValidationRule.swift index 2d49996..b9b5a24 100644 --- a/SwiftValidator/Rules/ValidationRule.swift +++ b/SwiftValidator/Rules/ValidationRule.swift @@ -18,7 +18,9 @@ public class ValidationRule { public var errorLabel:UILabel? /// the rules of the field public var rules:[Rule] = [] - public var remoteURLString: String? + /// tuple that holds remote validatin info + public var remoteInfo: (urlString: String, error: String)? + //public var remoteURLString: String? /** Initializes `ValidationRule` instance with text field, rules, and errorLabel. @@ -32,7 +34,14 @@ public class ValidationRule { self.textField = textField self.errorLabel = errorLabel self.rules = rules - self.remoteURLString = remoteURLString + //self.remoteURLString = remoteURLString + } + + public init(textField: UITextField, rules:[Rule], errorLabel:UILabel?, remoteInfo: (String, String)? = nil){ + self.textField = textField + self.errorLabel = errorLabel + self.rules = rules + self.remoteInfo = remoteInfo } /** diff --git a/Validator/ViewController.swift b/Validator/ViewController.swift index f273e1a..c2d22ca 100644 --- a/Validator/ViewController.swift +++ b/Validator/ViewController.swift @@ -50,22 +50,38 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate }) validator.registerField(fullNameTextField, errorLabel: fullNameErrorLabel , rules: [RequiredRule(), FullNameRule()]) - validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule()]) + //validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule()], remoteURLString: "www.yourdomain.com/path/to/emails/") + validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule()], remoteInfo: (urlString: "http://localhost:8000/", error: "Email already taken")) validator.registerField(emailConfirmTextField, errorLabel: emailConfirmErrorLabel, rules: [RequiredRule(), ConfirmationRule(confirmField: emailTextField)]) validator.registerField(phoneNumberTextField, errorLabel: phoneNumberErrorLabel, rules: [RequiredRule(), MinLengthRule(length: 9)]) validator.registerField(zipcodeTextField, errorLabel: zipcodeErrorLabel, rules: [RequiredRule(), ZipCodeRule()]) + validator.delegate = self } @IBAction func submitTapped(sender: AnyObject) { print("Validating...") - validator.validate(self) + validator.validate() + } + + func simulateRemoteRequest(seconds: Int64, completion: (result: Bool) -> Void) { + // Set number of seconds before "request" is finished + let triggerTime = (Int64(NSEC_PER_SEC) * seconds) + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, triggerTime), dispatch_get_main_queue(), { () -> Void in + // For example purposes, if user does not enter David Patterson as full name, + // the email they tried will be already taken + if self.fullNameTextField.text! == "David Patterson" { + completion(result: true) + } else { + completion(result: false) + } + }) } // MARK: ValidationDelegate Methods func validationSuccessful() { print("Validation Success!") - let alert = UIAlertController(title: "Success", message: "You are validated!", preferredStyle: UIAlertControllerStyle.Alert) + let alert = UIAlertController(title: "Success", message: "You are validated, \(fullNameTextField.text!)!", preferredStyle: UIAlertControllerStyle.Alert) let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil) alert.addAction(defaultAction) self.presentViewController(alert, animated: true, completion: nil) @@ -75,6 +91,14 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate print("Validation FAILED!") } + func remoteValidationRequest(text: String, urlString: String, completion: (result: Bool) -> Void) { + simulateRemoteRequest(5) { result -> Void in + // Set result to true if field was validated server-side + // Set to false if field was not validated server-side + completion(result: result) + } + } + func hideKeyboard(){ self.view.endEditing(true) } From d359bf06830f3d03e6f828eb8d631457391f8d38 Mon Sep 17 00:00:00 2001 From: David Patterson Date: Tue, 1 Mar 2016 19:33:45 -0600 Subject: [PATCH 3/3] remote validation added to Demo on email field --- README.md | 5 ----- SwiftValidator/Core/Validator.swift | 31 +++++++++++++++++------------ Validator/ViewController.swift | 15 +++++++------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index d86f007..c4525a1 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,4 @@ SwiftValidator -=============== - -[![Build Status](https://travis-ci.org/jpotts18/SwiftValidator.svg?branch=travis-ci)](https://travis-ci.org/jpotts18/SwiftValidator) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) -[![Coverage Status](https://coveralls.io/repos/github/davepatterson/SwiftValidator/badge.svg?branch=master)](https://coveralls.io/github/davepatterson/SwiftValidator?branch=travis-edit) - ======= [![Build Status](https://travis-ci.org/jpotts18/SwiftValidator.svg?branch=travis-ci)](https://travis-ci.org/jpotts18/SwiftValidator) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![codecov.io](https://codecov.io/github/jpotts18/SwiftValidator/coverage.svg?branch=master)](https://codecov.io/github/jpotts18/SwiftValidator?branch=master) diff --git a/SwiftValidator/Core/Validator.swift b/SwiftValidator/Core/Validator.swift index 7b4123a..afc7895 100644 --- a/SwiftValidator/Core/Validator.swift +++ b/SwiftValidator/Core/Validator.swift @@ -11,7 +11,7 @@ import UIKit /** Class that makes `Validator` objects. Should be added as a parameter to ViewController that will display validation fields. - */ +*/ public class Validator { /// Dictionary to hold all fields (and accompanying rules) that will undergo validation. public var validations = [UITextField:ValidationRule]() @@ -54,6 +54,7 @@ public class Validator { } } } + /** This method is used to validate all fields registered to Validator. If validation is unsuccessful, field gets added to errors dictionary. Completion closure is used to validator know when all fields @@ -83,6 +84,7 @@ public class Validator { } } } + /** This method is used to validate a field that will need to also be validated via remote request. - parameter textField: TextField of field that is being validated. @@ -92,12 +94,10 @@ public class Validator { */ private func validateRemoteField(textField: UITextField, completion: (status: Bool) -> Void) { if let fieldRule = validations[textField] { - // Carry on with validation as remote validation passed + // Carry on with validation as regular validation passed if self.validateRegularField(fieldRule.textField) { delegate!.remoteValidationRequest!(textField.text!, urlString: fieldRule.remoteInfo!.urlString, completion: { result -> Void in if result { - // Carry on with validation as remote validation passed - //self.validateRegularField(fieldRule.textField) if let transform = self.successStyleTransform { transform(validationRule: fieldRule) } @@ -110,21 +110,23 @@ public class Validator { transform(validationError: error) } } + // Validation is over, so let validateAllFields(completion: (status: Bool)) know completion(status: true) }) } else { - // Fail validation + // Validation is over, so let validateAllFields(completion: (status: Bool)) know completion(status: true) } } } + /** Method used to validate a regular field (non-remote). - parameter: TextField of field that is undergoing validation - returns: A Bool that represents whether the validation was a success or failure, returns true for the former and false for the latter. - */ + */ private func validateRegularField(textField: UITextField) -> Bool { if let fieldRule = validations[textField] { if let error = fieldRule.validateField() { @@ -192,7 +194,7 @@ public class Validator { - parameter textField: field that is to be validated. - parameter Rule: An array which holds different rules to validate against textField. - returns: No return value - */ + */ public func registerField(textField:UITextField, rules:[Rule], remoteInfo: (String, String)? = nil) { validations[textField] = ValidationRule(textField: textField, rules: rules, errorLabel: nil, remoteInfo: remoteInfo) } @@ -204,8 +206,7 @@ public class Validator { - parameter errorLabel: A UILabel that holds error label data - parameter rules: A Rule array that holds different rules that apply to said textField. - returns: No return value - */ - + */ public func registerField(textField:UITextField, errorLabel:UILabel, rules:[Rule], remoteInfo: (String, String)? = nil) { validations[textField] = ValidationRule(textField: textField, rules:rules, errorLabel:errorLabel, remoteInfo: remoteInfo) } @@ -215,7 +216,7 @@ public class Validator { - parameter textField: field used to locate and remove textField from validator. - returns: No return value - */ + */ public func unregisterField(textField:UITextField) { validations.removeValueForKey(textField) errors.removeValueForKey(textField) @@ -225,7 +226,7 @@ public class Validator { This method checks to see if all fields in validator are valid. - returns: No return value. - */ + */ public func validate(delegate:ValidationDelegate) { self.validateAllFields() @@ -238,6 +239,10 @@ public class Validator { } + /** + This method attempts to validate all fields registered to Validator(). + - returns: No return value. + */ public func validate() { self.validateAllFields { finished -> Void in if self.errors.isEmpty { @@ -253,12 +258,12 @@ public class Validator { } /** - This method validates all fields in validator and sets any errors to errors parameter of callback. + This method validates all fields in validator and sets any errors to errors parameter of closure. - parameter callback: A closure which is called with errors, a dictionary of type UITextField:ValidationError. - returns: No return value. - */ + */ public func validate(callback:(errors:[UITextField:ValidationError])->Void) -> Void { self.validateAllFields() diff --git a/Validator/ViewController.swift b/Validator/ViewController.swift index c2d22ca..697ec73 100644 --- a/Validator/ViewController.swift +++ b/Validator/ViewController.swift @@ -50,8 +50,7 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate }) validator.registerField(fullNameTextField, errorLabel: fullNameErrorLabel , rules: [RequiredRule(), FullNameRule()]) - //validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule()], remoteURLString: "www.yourdomain.com/path/to/emails/") - validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule()], remoteInfo: (urlString: "http://localhost:8000/", error: "Email already taken")) + validator.registerField(emailTextField, errorLabel: emailErrorLabel, rules: [RequiredRule(), EmailRule()], remoteInfo: (urlString: "http://localhost:8000/emails/", error: "Email already in use")) validator.registerField(emailConfirmTextField, errorLabel: emailConfirmErrorLabel, rules: [RequiredRule(), ConfirmationRule(confirmField: emailTextField)]) validator.registerField(phoneNumberTextField, errorLabel: phoneNumberErrorLabel, rules: [RequiredRule(), MinLengthRule(length: 9)]) validator.registerField(zipcodeTextField, errorLabel: zipcodeErrorLabel, rules: [RequiredRule(), ZipCodeRule()]) @@ -64,6 +63,7 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate } func simulateRemoteRequest(seconds: Int64, completion: (result: Bool) -> Void) { + print("Simulating \(seconds) second server request...") // Set number of seconds before "request" is finished let triggerTime = (Int64(NSEC_PER_SEC) * seconds) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, triggerTime), dispatch_get_main_queue(), { () -> Void in @@ -76,6 +76,10 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate } }) } + + func hideKeyboard(){ + self.view.endEditing(true) + } // MARK: ValidationDelegate Methods @@ -87,22 +91,19 @@ class ViewController: UIViewController , ValidationDelegate, UITextFieldDelegate self.presentViewController(alert, animated: true, completion: nil) } + func validationFailed(errors:[UITextField:ValidationError]) { print("Validation FAILED!") } func remoteValidationRequest(text: String, urlString: String, completion: (result: Bool) -> Void) { - simulateRemoteRequest(5) { result -> Void in + simulateRemoteRequest(2) { result -> Void in // Set result to true if field was validated server-side // Set to false if field was not validated server-side completion(result: result) } } - func hideKeyboard(){ - self.view.endEditing(true) - } - // MARK: Validate single field // Don't forget to use UITextFieldDelegate func textFieldShouldReturn(textField: UITextField) -> Bool {