Instantly share code, notes, and snippets.

Embed
What would you like to do?
GameplayKit GKRuleSystem Validator
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