GRValidation 0.2.0

GRValidation 0.2.0

测试已测试
语言语言 SwiftSwift
许可协议 MIT
发布最后发布2015年9月
SPM支持 SPM

Gwendal Roué 维护。



  • 作者
  • Gwendal Roué

GRValidation

GRValidation 是 Swift 2 的验证工具集。

它允许您验证简单的值和复杂模型,当验证超出简单领域时,它也不会让您失望。

2015年8月25日:GRValidation 0.2.0 发布 - 发布说明。关注 Twitter 上的 @groue 获取发布公告和使用提示。

特点

  • 类型安全
  • 值验证
  • 值修复
  • 复杂模型验证
  • 详细的错误信息

目前缺少的功能

  • 验证错误的本地化
  • 验证错误的内省

值验证与模型验证比较

GRValidation 区分了 值验证模型验证

精确地说,值验证 抛出错误,如“12 应该大于 18”,负责以下操作:

  • 值检查:例如,“这个字符串是否为空?”
  • 值修复:例如,让我们修剪、验证和格式化用户提供的电话号码。
  • 组合:可以将多个验证组合成一个更复杂的单个验证,例如“这个字符串应该是 nil,或者不为空”。

模型验证则相反,抛出错误,如“年龄应该大于 18”。它在值验证之上构建并提供

  • 属性验证:例如,“名字不能为空”。
  • 全局验证:例如,“请提供一个电子邮件或电话号码”。
  • 可变验证。这是值修复的应用示例:例如,在成功验证一个用户提供的电话号码后,希望存储其格式化版本。

文档

值验证

The ValidationType 协议

ValidationType 是一种协议,用于检查类型为 TestedType 的值,并最终返回类型为 ValidType 的值,或者在抛出 ValidationError

public protocol ValidationType {
    typealias TestedType
    typealias ValidType
    func validate(value: TestedType) throws -> ValidType
}

例如

// Positive integer
let v = ValidationRange(minimum: 0)
try v.validate(1)   // OK: 1
try v.validate(nil) // ValidationError: nil should be greater than or equal to 0.
try v.validate(-1)  // ValidationError: -1 should be greater than or equal to 0.

返回的值可能与输入不同

enum Color : Int {
    case Red
    case White
    case Rose
}
let v = ValidationRawValue<Color>()
try v.validate(0)   // OK: Color.Red
try v.validate(3)   // ValidationError: 3 is an invalid Color.

ValidationError 与布尔检查的比较

validate() 方法可能抛出 ValidationError

let positiveInt = ValidationRange(minimum: 0)
try positiveInt.validate(-1)    // Throws a ValidationError

您还可以使用 ~= 运算符或通过模式匹配执行简单的布尔检查

positiveInt ~= 10  // true
positiveInt ~= -1  // false

switch int {
case positiveInt:
    // int passes validation
    ...
}

内置值验证

验证类型 TestedType ValidType
验证 T T 所有值都通过。
ValidationFailure T T 所有值都失败。
ValidationNil T? T? 检查输入是否为 nil。
ValidationNotNil T? T 检查输入是否不为 nil。
ValidationTrim String? String? 所有字符串都通过。非 nil 字符串将被修剪。
ValidationStringLength String? String 检查字符串不为 nil 且长度在特定范围内。
ValidationRegularExpression String? String 检查字符串不为 nil 并匹配正则表达式。
ValidationCollectionNotEmpty CollectionType? CollectionType 检查集合不为 nil 且不为空。
ValidationCollectionEmpty CollectionType? CollectionType? 检查集合为 nil 或为空。
ValidationEqual T? where T:Equatable T 检查值不为 nil 且等于引用值。
ValidationNotEqual T? where T:Equatable T? 检查值是 nil 或不等于引用值。
ValidationElementOf T? where T:Equatable T 检查值不为 nil 并且是引用集合的成员。
ValidationNotElementOf T? where T:Equatable T? 检查值是 nil 或不是引用集合的成员。
ValidationRawValue T.RawValue? where T: RawRepresentable T 检查值不为 nil 并且是类型 T 的有效原始值。
ValidationRange T? where T: ForwardIndexType, T: Comparable T 检查值不为 nil 并且在特定范围内。

组合验证

基本值验证可以链式连接,或使用布尔运算符组合

  • v1 >>> v2

    连接两个验证。返回 v2 返回的值。

    // Checks that a string matches a regular expression, after trimming:
    let v = ValidationTrim() >>> ValidationRegularExpression(pattern: "^[0-9]+$")
    try v.validate(" 123 ") // "123"
    try v.validate("foo")   // ValidationError
  • v1 || v2

    检查值通过至少一个验证。返回第一个通过验证的值返回的值,或者在输出类型不匹配时返回输入值。

    // Checks that an Int is not nil and equal to 1 or 2:
    let v = ValidationEqual(1) || ValidationEqual(2)
    try v.validate(1) // 1
    try v.validate(2) // 2
    try v.validate(3) // ValidationError
  • v1 && v2

    检查值通过两个验证。返回 v2 返回的值。

    // Checks that an Int is nil, or not 1, and not 2:
    let v = ValidationNotEqual(1) && ValidationNotEqual(2)
    try v.validate(1) // ValidationError
    try v.validate(2) // ValidationError
    try v.validate(3) // 3
  • !v1

    反转验证。返回输入值,或者抛出一个通用的“is invalid.”错误。

    // Checks that an Int is not 1.
    let v = !ValidationEqual(1)
    try v.validate(1) // ValidationError
    try v.validate(2) // 2

模型验证

The Validable 协议

The Validable 协议提供了帮助验证模型的方法。

让我们从一个简单的模型开始

struct Person: Validable {
    var name: String?

    func validate() throws {
        // Name should not be nil or empty.
        try validate(property: "name", with: name >>> ValidationStringLength(minimum: 1))
    }
}

let person = Person(name: "Arthur")
try person.validate()   // OK

let person = Person(name: nil)
try person.validate()
// Invalid Person(name: nil): name should not be empty.

请随意,不要犹豫构建更复杂的验证

struct Person : Validable {
    var name: String?
    var age: Int?
    var email: String?
    var phoneNumber: String?

    mutating func validate() throws {
        // ValidationPlan doesn't fail on the first validation error. Instead,
        // it gathers all of them, and eventually throws a single ValidationError.
        try ValidationPlan()
            .append {
                // Name should not be empty after whitespace trimming:
                let nameValidation = ValidationTrim() >>> ValidationStringLength(minimum: 1)
                name = try validate(
                    property: "name",
                    with: name >>> nameValidation)
            }
            .append {
                // Age should be nil, or positive:
                let ageValidation = ValidationNil() || ValidationRange(minimum: 0)
                try validate(
                    property: "age",
                    with: age >>> ageValidation)
            }
            .append {
                // Email should be nil, or contain @ after whitespace trimming:
                let emailValidation = ValidationNil() || (ValidationTrim() >>> ValidationRegularExpression(pattern:"@"))
                email = try validate(
                    property: "email",
                    with: email >>> emailValidation)
            }
            .append {
                // Phone number should be nil, or be a valid phone number.
                // ValidationPhoneNumber applies international formatting.
                let phoneNumberValidation = ValidationNil() || (ValidationTrim() >>> ValidationPhoneNumber(format: .International))
                phoneNumber = try validate(
                    property: "phoneNumber",
                    with: phoneNumber >>> phoneNumberValidation)
            }
            .append {
                // An email or a phone number is required.
                try validate(
                    properties: ["email", "phoneNumber"],
                    message: "Please provide an email or a phone number.",
                    with: email >>> ValidationNotNil() || phoneNumber >>> ValidationNotNil())
            }
            .validate()
    }
}

var person = Person(name: " Arthur ", age: 35, email: nil, phoneNumber: "0123456789  ")
try person.validate()   // OK
person.name             // "Arthur" (trimmed)
person.phoneNumber      // "+33 1 23 45 67 89" (trimmed & formatted)

var person = Person(name: nil, age: nil, email: "[email protected]", phoneNumber: nil)
try person.validate()
// Invalid Person: name should not be empty.

var person = Person(name: "Arthur", age: -1, email: "[email protected]", phoneNumber: nil)
try person.validate()
// Invalid Person: age should be greater than or equal to 0.

var person = Person(name: "Arthur", age: 35, email: nil, phoneNumber: nil)
try person.validate()
// Invalid Person: Please provide an email or a phone number.

var person = Person(name: "Arthur", age: 35, email: "foo", phoneNumber: nil)
try person.validate()
// Invalid Person: email is invalid.