let constraint = TypeConstraint<Account, Account.Error> {
KeyPathConstraint(\.username) {
BlockConstraint {
$0.count >= 5
} errorBuilder: {
.username
}
}
KeyPathConstraint(\.password) {
GroupConstraint(.all) {
PredicateConstraint {
.characterSet(.lowercaseLetters, mode: .inclusive)
} errorBuilder: {
.password(.missingLowercase)
}
PredicateConstraint{
.characterSet(.uppercaseLetters, mode: .inclusive)
} errorBuilder: {
.password(.missingUppercase)
}
PredicateConstraint {
.characterSet(.decimalDigits, mode: .inclusive)
} errorBuilder: {
.password(.missingDigits)
}
PredicateConstraint {
.characterSet(CharacterSet(charactersIn: "!?@#$%^&*()|\\/<>,.~`_+-="), mode: .inclusive)
} errorBuilder: {
.password(.missingSpecialChars)
}
PredicateConstraint {
.length(min: 8)
} errorBuilder: {
.password(.tooShort)
}
}
}
BlockConstraint {
$0.password == $0.passwordConfirmation
} errorBuilder: {
.password(.confirmationMismatch)
}
KeyPathConstraint(\.email) {
PredicateConstraint(.email, error: .email)
}
KeyPathConstraint(\.age) {
PredicateConstraint(.range(min: 14), error: .underAge)
}
KeyPathConstraint(\.website) {
PredicateConstraint(.url, error: .website)
.optional()
}
}
let result = constraint.evaluate(with: account)
switch result {
case .success:
handleSuccess()
case .failure(let summary):
handleErrors(summary.errors)
}
Peppermint
是一个声明式的、轻量级的数据验证框架。
其核心有两个原则:
每个项目在挑战上都是独一无二的,如果我们能专注于解决这些挑战,而不是把时间花在样板任务上,那就太好了。
考虑到这一点,该框架遵循面向协议编程的范式,并由一小组协议和结构设计而成,可以轻松地组合以满足您的项目需求。 因此,您可以将 Peppermint
视为一个可调节的扳手,而不是瑞士军刀。
由于验证可以在多个级别进行,因此 Peppermint
可用于 iOS、macOS、tvOS、watchOS 和原生 Swift 项目,例如服务器端应用程序。
Peppermint
仅通过 Swift Package Manager 提供。
您可以通过转到 File > Swift Packages > Add Package Dependency
,在 Xcode 中将 Peppermint
添加到您的项目中。
或者,如果您想将其用作您自己软件包的依赖项,您可以将其添加到您的 Package.swift
文件中
import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
targets: [],
dependencies: [
.Package(url: "https://github.com/nsagora/peppermint", majorVersion: 1),
]
)
有关全面的示例列表,请尝试 Examples.playground
Examples
playgroundPeppermint
框架紧凑,为您提供了围绕项目需求构建数据验证所需的基础。 此外,它还包括一组常见的验证谓词和约束,大多数项目都可以从中受益。
Predicate
代表核心 protocol
,其作用是 evaluate
输入是否匹配给定的验证条件。
在 Peppermint
的核心,以下有两个谓词,允许您组合特定于项目需求的谓词
let predicate = BlockPredicate<String> { $0.characters.count > 2 }
predicate.evaluate(with: "a") // returns false
predicate.evaluate(with: "abc") // returns true
let predicate = RegexPredicate(expression: "^[a-z]$")
predicate.evaluate(with: "a") // returns true
predicate.evaluate(with: "5") // returns false
predicate.evaluate(with: "ab") // returns false
此外,该框架提供了一组常见的验证谓词,您的项目可以从中受益
let predicate = EmailPredicate()
predicate.evaluate(with: "hello@") // returns false
predicate.evaluate(with: "hello@nsagora.com") // returns true
predicate.evaluate(with: "héllo@nsagora.com") // returns true
let predicate = URLPredicate()
predicate.evaluate(with: "http://www.url.com") // returns true
predicate.evaluate(with: "http:\\www.url.com") // returns false
let predicate = let range = RangePredicate(10...20)
predicate.evaluate(with: 15) // returns true
predicate.evaluate(with: 21) // returns false
let predicate = LengthPredicate<String>(min: 5)
predicate.evaluate(with: "abcde") // returns true
predicate.evaluate(with: "abcd") // returns false
最重要的是,开发人员可以通过扩展 Predicate
协议,和/或通过组合或装饰现有谓词来构建更高级或更复杂的谓词
public struct CustomPredicate: Predicate {
public typealias InputType = String
private let custom: String
public init(custom: String) {
self.custom = custom
}
public func evaluate(with input: String) -> Bool {
return input == custom
}
}
let predicate = CustomPredicate(custom: "alphabet")
predicate.evaluate(with: "alp") // returns false
predicate.evaluate(with: "alpha") // returns false
predicate.evaluate(with: "alphabet") // returns true
PredicateConstraint
表示一种将 Predicate
链接到 Error
的数据类型,以便为最终用户提供有用的反馈。
let constraint = PredicateConstraint<String, MyError>(.email, error: .invalid)
let result = constraint.evaluate(with: "hello@nsagora.com")
switch result {
case .valid:
print("Hi there 👋!")
case .invalid(let summary):
print("Oh, I was expecting a valid email address!")
} // prints "Hi there 👋!"
enum MyError: Error {
case invalid
}
BlockConstraint
表示一种将自定义验证闭包链接到 Error
的数据类型,用于描述评估失败的原因。 它是使用 BlockPredicate
初始化的 PredicateConstraint
的快捷方式。
let constraint = BlockConstraint<Int, MyError> {
$0 % 2 == 0
} errorBuilder: {
.magicNumber
}
constraint.evaluate(with: 3)
enum Failure: MyError {
case magicNumber
}
GroupConstraint
表示约束的组合,允许在以下情况下进行评估
为了提供上下文,GroupConstraint
允许我们将一段数据约束为必需的,并且是有效的电子邮件。
Peppermint
试图整合、标准化并明确用于验证用户输入的逻辑。为此,下面的示例演示了如何构建一个完整的 GroupConstraint
对象,该对象可用于强制执行用户密码数据的要求
var passwordConstraint = GroupConstraint<String, Form.Password>(.all) {
PredicateConstraint {
.characterSet(.lowercaseLetters, mode: .loose)
} errorBuilder: {
.missingLowercase
}
PredicateConstraint{
.characterSet(.uppercaseLetters, mode: .loose)
} errorBuilder: {
.missingUppercase
}
PredicateConstraint {
.characterSet(.decimalDigits, mode: .loose)
} errorBuilder: {
.missingDigits
}
PredicateConstraint {
.characterSet(CharacterSet(charactersIn: "!?@#$%^&*()|\\/<>,.~`_+-="), mode: .loose)
} errorBuilder: {
.missingSpecialChars
}
PredicateConstraint {
.length(min: 8)
} errorBuilder: {
.minLength(8)
}
}
let password = "3nGuard!"
let result = passwordConstraint.evaluate(with: password)
switch result {
case .success:
print("Wow, that's a 💪 password!")
case .failure(let summary):
print(summary.errors.map({$0.localizedDescription}))
} // prints "Wow, that's a 💪 password!"
从上面我们可以看到,一旦我们构建了 passwordConstraint
,我们只是调用 evaluate(with:)
来获取我们的评估 Result
。 这包含一个可以随意处理的 Summary
。
我们非常感谢您为 Peppermint 做出贡献,请查看 LICENSE
文件以获取更多信息。
该项目由罗马尼亚 Iași 的 iOS 开发者社区 iOS NSAgora 的成员开发和维护。