GitHub License GitHub Tag Swift Package Manager Compatible Written in Swift Supported platforms Build Status

ValidatableValue

带有内置声明式验证的通用值包装器。

问题

每个应用程序都有数据模型。数据模型通常实现为自定义结构化的数据类型,用于存储一个或多个属性。理想情况下,每个属性的类型(无论是原始类型还是结构化类型)都定义了该属性所有可能值的期望集合。此外,可能存在特殊规则,用于定义任何给定值对于该属性是否可接受。

Swift 没有内置机制来缩小标准数据类型中允许值的集合,也无法评估针对每个给定值的任何特殊规则,以检查它对于该属性是否可接受。

例如,要将一个值限制为 1 到 100 范围内的整数,并避免该范围内的所有奇数,我们通常只使用 Integer,然后在实际将值放入此属性之前,以某种方式实现所需的检查。 这导致业务逻辑的单个部分(对此特定属性的要求)分布在代码库中至少两个(有时更多)位置。

愿望清单

  1. 简洁的声明式值验证逻辑定义;
  2. 在实际将值写入属性之前进行安全的值验证;
  3. 能够将多个要求组合在一起以描述复杂的验证规则;
  4. 通过使验证逻辑编写为类型级别的纯函数来消除任何副作用。

如何安装

推荐使用 SwiftPM 进行安装,但也支持开箱即用的 Carthage

快速示例

为了描述用户帐户密码的自定义验证规则,让我们定义所谓的 ValueSpecification。 此规范定义值基本类型(任何预定义的数据类型)以及需要满足的一组要求才能通过验证。

enum Password: ValueSpecification
{
    static
    let conditions: [Condition<String>] = [

        Check("Lenght between 8 and 30 characters"){ 8...30 ~= $0.count },
        Check("Has at least 1 capital character"){ 1 <= Pwd.caps.count(in: $0) },
        Check("Has at least 4 lower characters"){ 4 <= Pwd.lows.count(in: $0) },
        Check("Has at least 1 digit character"){ 1 <= Pwd.digits.count(in: $0) },
        Check("Has at least 1 special character"){ 1 <= Pwd.specials.count(in: $0) },
        Check("Allowed chars only"){ Pwd.allowed.isSuperset(of: CS(charactersIn: $0)) }
    ]
}

请注意,Password 被声明为枚举,因为我们只需要它作为范围,它不会被直接实例化——它的名称定义了规范的名称,并且在内部我们指定了必须满足的 conditions(又名要求)才能通过验证。

在上面的例子中,我们使用像这样定义的简单自定义辅助函数

enum Pwd
{
    static
    let caps = CS.uppercaseLetters

    static
    let lows = CS.lowercaseLetters

    static
    let digits = CS.decimalDigits

    static
    let specials = CS(charactersIn: " ,.!?@#$%^&*()-_+=")

    static
    var allowed = caps.union(lows).union(digits).union(specials)
}

如何使用

请参阅 User.swift 中的 单元测试,以获取最新的综合使用示例。