Last active
December 7, 2016 11:51
-
-
Save oliverfoggin/19b2838e7b5149d307bafac99caed7b6 to your computer and use it in GitHub Desktop.
GameplayKit GKRuleSystem 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 | |
import GameplayKit | |
enum ValidationResult<T: Error> { | |
case valid | |
case invalid(error: T) | |
} | |
class Fact<T: Error>: NSObject { | |
let error: T | |
let salience: Int | |
let evaluationBlock: (_ system: GKRuleSystem) -> (Bool) | |
init(error: T, salience: Int = 0, evaluationBlock: @escaping (_ system: GKRuleSystem) -> (Bool)) { | |
self.error = error | |
self.evaluationBlock = evaluationBlock | |
self.salience = salience | |
} | |
} | |
class FactBasedRule<T: Error>: GKRule { | |
let fact: Fact<T> | |
init(fact: Fact<T>) { | |
self.fact = fact | |
super.init() | |
salience = fact.salience | |
} | |
override func evaluatePredicate(in system: GKRuleSystem) -> Bool { | |
return fact.evaluationBlock(system) | |
} | |
override func performAction(in system: GKRuleSystem) { | |
system.assertFact(fact) | |
} | |
} | |
enum PasswordError: Error { | |
case passwordsDontMatch | |
case notLongEnough | |
case uppercaseMissing | |
case lowerCaseMissing | |
case noNumbers | |
case spacesNotAllowed | |
} | |
struct PasswordValidator { | |
let lengthFact = Fact(error: PasswordError.notLongEnough) { | |
system in | |
guard let password = system.state["password"] as? String, | |
password.characters.count >= 8 else { | |
return false | |
} | |
return true | |
} | |
let passwordsMatchFact = Fact(error: PasswordError.passwordsDontMatch, salience: 1) { | |
system in | |
guard let password = system.state["password"] as? String, | |
let confirmedPass = system.state["confirmedPassword"] as? String, | |
password == confirmedPass else { | |
return false | |
} | |
return true | |
} | |
let uppercaseLetterFact = Fact(error: PasswordError.uppercaseMissing) { | |
system in | |
guard let password = system.state["password"] as? String, | |
password.rangeOfCharacter(from: .uppercaseLetters) != nil else { | |
return false | |
} | |
return true | |
} | |
let lowercaseLetterFact = Fact(error: PasswordError.lowerCaseMissing) { | |
system in | |
guard let password = system.state["password"] as? String, | |
password.rangeOfCharacter(from: .lowercaseLetters) != nil else { | |
return false | |
} | |
return true | |
} | |
let numberFact = Fact(error: PasswordError.noNumbers) { | |
system in | |
guard let password = system.state["password"] as? String, | |
password.rangeOfCharacter(from: .decimalDigits) != nil else { | |
return false | |
} | |
return true | |
} | |
let noWhiteSpaceFact = Fact(error: PasswordError.spacesNotAllowed) { | |
system in | |
guard let password = system.state["password"] as? String, | |
password.rangeOfCharacter(from: .whitespacesAndNewlines) == nil else { | |
return false | |
} | |
return true | |
} | |
func validatePasswords(password: String, confirmedPassword: String) -> ValidationResult<PasswordError> { | |
let facts = [lengthFact, passwordsMatchFact, uppercaseLetterFact, lowercaseLetterFact, numberFact, noWhiteSpaceFact] | |
let ruleSystem = GKRuleSystem() | |
ruleSystem.add(facts.map(FactBasedRule.init)) | |
ruleSystem.state["password"] = password | |
ruleSystem.state["confirmedPassword"] = confirmedPassword | |
ruleSystem.evaluate() | |
let error = facts.filter { ruleSystem.grade(forFact: $0) == 0 } | |
.sorted { $0.salience > $1.salience } | |
.map { $0.error } | |
.first | |
if let error = error { | |
return .invalid(error: error) | |
} else { | |
return .valid | |
} | |
} | |
} | |
PasswordValidator().validatePasswords(password: "Hell ld1", confirmedPassword: "Hell ld1") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment