Created
December 6, 2016 18:54
-
-
Save to4iki/a16ce321fbf7a0346ad77c62e630b48f to your computer and use it in GitHub Desktop.
composite validator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// MARK: - EmailValidationError | |
enum EmailValidationError: Error { | |
case empty | |
case invalidFormat | |
} | |
// MARK: - PasswordValidationError | |
enum PasswordValidationError: Error { | |
case empty | |
case tooShort | |
case noUppercaseLetter | |
case noLowercaseLetter | |
case noNumber | |
} | |
// MARK: - Validators | |
struct EmptyStringValidator: Validator { | |
private let invalidError: Error | |
init(invalidError: Error) { | |
self.invalidError = invalidError | |
} | |
func validate(_ value: String) -> ValidationResult { | |
if value.isEmpty { | |
return .invalid(errors: [invalidError]) | |
} else { | |
return .valid | |
} | |
} | |
} | |
struct EmailFormatValidator: Validator { | |
func validate(_ value: String) -> ValidationResult { | |
let magicEmailRegexStolenFromTheInternet = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" | |
let emailTest = NSPredicate(format:"SELF MATCHES %@", magicEmailRegexStolenFromTheInternet) | |
if emailTest.evaluate(with: value) { | |
return .valid | |
} else { | |
return .invalid(errors: [EmailValidationError.invalidFormat]) | |
} | |
} | |
} | |
struct PasswordLengthValidator: Validator { | |
func validate(_ value: String) -> ValidationResult { | |
if value.characters.count >= 8 { | |
return .valid | |
} else { | |
return .invalid(errors: [PasswordValidationError.tooShort]) | |
} | |
} | |
} | |
struct UppercaseLetterValidator: Validator { | |
func validate(_ value: String) -> ValidationResult { | |
let uppercaseLetterRegex = ".*[A-Z]+.*" | |
let uppercaseLetterTest = NSPredicate(format:"SELF MATCHES %@", uppercaseLetterRegex) | |
if uppercaseLetterTest.evaluate(with: value) { | |
return .valid | |
} else { | |
return .invalid(errors: [PasswordValidationError.noUppercaseLetter]) | |
} | |
} | |
} | |
// MARK: - ValidatorFactory | |
struct ValidatorFactory { | |
static let sharedInstance = ValidatorFactory() | |
private init() {} | |
func emailValidator() -> Validator { | |
return CompositeValidator(validators: emptyEmailStringValidator(), EmailFormatValidator()) | |
} | |
func passwordValidator() -> Validator { | |
return CompositeValidator(validators: emptyPasswordStringValidator(), passwordStrengthValidator()) | |
} | |
private func emptyEmailStringValidator() -> Validator { | |
return EmptyStringValidator(invalidError: EmailValidationError.empty) | |
} | |
private func emptyPasswordStringValidator() -> Validator { | |
return EmptyStringValidator(invalidError: PasswordValidationError.empty) | |
} | |
private func passwordStrengthValidator() -> Validator { | |
return CompositeValidator(validators: PasswordLengthValidator(), UppercaseLetterValidator()) | |
} | |
} | |
// MARK: - Exectute | |
let emailValidator = ValidatorFactory.sharedInstance.emailValidator() | |
print(emailValidator.validate("")) | |
print(emailValidator.validate("invalidEmail@")) | |
print(emailValidator.validate("validEmail@validDomain.com")) | |
let passwordValidator = ValidatorFactory.sharedInstance.passwordValidator() | |
print(passwordValidator.validate("")) | |
print(passwordValidator.validate("psS$")) | |
print(passwordValidator.validate("paSSw0rd")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// MARK: - ValidationResult | |
public enum ValidationResult { | |
case valid | |
case invalid(errors: [Error]) | |
} | |
// MARK: - Validator | |
public protocol Validator { | |
func validate(_ value: String) -> ValidationResult | |
} | |
// MARK: - CompositeValidator | |
public struct CompositeValidator: Validator { | |
private let validators: [Validator] | |
public init(validators: Validator...) { | |
self.validators = validators | |
} | |
public func validate(_ value: String) -> ValidationResult { | |
return validators.reduce(.valid) { (acc, validator) -> ValidationResult in | |
switch validator.validate(value) { | |
case .valid: | |
return acc | |
case .invalid(let errors): | |
switch acc { | |
case .valid: | |
return .invalid(errors: errors) | |
case .invalid(errors: let accErrors): | |
return .invalid(errors: accErrors + errors) | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment