PrediKit 4.0.0

PrediKit 4.0.0

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

Hector Matos 维护。



PrediKit 4.0.0

Pretty Banner

PrediKit

一个受 SnapKit 启发的用于 iOS 和 OS X 的 Swift NSPredicate DSL,由 KrakenDev 的人精心编写并创建。

如果您熟悉 SnapKit API 的直观性,那么您会喜欢 PrediKit 的感觉!💖

文档

文档由 Jazzy 生成,您可以通过此链接方便地找到它:这里

为什么创建 PrediKit?

因为我想要这样!另外,因为创建 NSPredicate 很困难。当与 CoreData 一起工作时,您使用 NSPredicates 来获取持久化的对象。虽然 CoreData 已经很复杂了,但为什么要再使用一个复杂的字符串基础预设系统呢?

用于创建预设格式的语言完全是基于字符串的,这可能导致一系列问题。

  • 我们倾向于拼写错误,并且在使用当前系统的情况下,您可以在不知道拼写属性名错误的情况下构建和运行...直到它变得太晚。
  • 对于许多 iOS/OSX 开发者(包括我自己),他们对与创建预设格式一起出现的类似 SQL 的语言可能非常陌生。实际上,由于这个原因, Realm 的出色团队创建了一个完整的 速查表
  • 对于复杂的预设创建,很容易出现字符串盲视。试着创建一个复杂的预设,然后经过几个小时后阅读它。我敢打赌你会做到的。😎
  • 如果在一个预设格式字符串中拼写了一个属性键名错误,但该字符串可以被 NSPredicate 系统解析,那么什么都不会发生。它只是默默失败。至少我认为是这样。我目前在睡两个小时后撰写这篇文章。请别引用我。

它解决了什么问题?

希望它解决了上述所有问题以及更多。目前,它

  • 为开发者提供了一种闭包方式来创建 NSPredicate。
  • 它还通过 Xcode 的魔法功能,提供了一种自动完成查询的方式。不再需要参考速查表。只需按点按钮并享受自动完成功能。
  • 我也精心构建了API,使其尽可能像一本书一样易于阅读。即使是匹配字符串,为了在指定字符串是否匹配不匹配另一个值时语法正确,也都有一个冗余的API。
  • 通过一点运行时魔法/反射,如果在运行时拼写一个属性键或提供一个不存在于特定类的属性列表中的属性键,PrediKit将会崩溃。
  • 所有的builder闭包都不需要捕获语义,因为每个闭包都是一个@noescape闭包。如果你不知道这意味着什么,请到这里阅读 🤓。

安装

PrediKit可以通过以下任何一种方法集成到你的项目中:

手动

如果你不愿使用上述依赖管理器,你可以手动将PrediKit集成到你的项目中。

首先,将这些命令复制并粘贴到终端

git clone https://github.com/KrakenDev/PrediKit.git
open PrediKit/Sources/

这将在Finder窗口中打开一个包含PrediKit所需重要文件的源文件夹。将这些文件夹拖放到你的项目中(最好放在名为“PrediKit”的文件夹中)并开始编写代码!由于你会直接将文件复制到项目中,因此在需要PrediKit的任何文件中都不需要import PrediKit行。

这种方式的不便之处在于,你不能轻松地更新PrediKit。每次你想要获取最新和最好的版本时,都需要重复这些步骤!😱

使用

PrediKit力求使创建NSPredicate变得简单。深受来自SnapKit的API的启发,PrediKit的API对我的爱者来说极为相似。看看这个。以下示例创建了一个用于从CoreData获取ManagedObject的谓词。

let predicate = NSPredicate(ManagedLegend.self) { includeIf in
    includeIf.string("title").equals("The Almighty Kraken")
}

检查属性是否为nil

let predicate = NSPredicate(ManagedLegend.self) { includeIf in
    includeIf.string("title").equalsNil()
}

PrediKit还可以查询成员属性。比如说,你有一个类似于这样的类结构

class Captain: NSObject {
    var name: String
}
class Ship: NSObject {
    var captain: Captain
}

并且你想创建这些谓词

let someCaptain = Captain()
NSPredicate(format: "captain == %@ && captain.name == 'Chief Supreme'", someCaptain)

使用PrediKit创建上述内容既简单又易于表达

let someCaptain = Captain()
let predicate = NSPredicate(Ship.self) { includeIf
    let includeIfShipCaptain = includeIf.member("captain", ofType: Captain.self)
    includeIfShipCaptain.equals(someCaptain) &&
    includeIfShipCaptain.string("name").equals("Chief Supreme")
}

PrediKit还重载了&&||!运算符。这允许你组合并指定是否包括你的includers(我知道这个名字很糟糕。欢迎提出建议)。

let predicate = NSPredicate(ManagedLegend.self) { includeIf in
    //Include any ManagedLegend instance if the property named "string" is NOT nil and does NOT equal "The Almighty Kraken"
    !includeIf.string("title").equalsNil() &&
    !includeIf.string("title").equals("The Almighty Kraken") &&

    //Also include any ManagedLegend instance if the date property named "birthdate" is in the past or if the bool property "isAwesome" is true.
    includeIf.date("birthdate").isEarlierThan(NSDate()) ||
    includeIf.bool("isAwesome").isTrue()
}

甚至可以条件性地创建includers

let predicate = NSPredicate(ManagedLegend.self) { includeIf in
    let isKrakenQuery = includeIf.string("title").equals("The Almighty Kraken")
    let birthdateQuery = includeIf.date("birthdate").isEarlierThan(NSDate())
    let isAwesomeQuery = includeIf.bool("isAwesome").isTrue

    if shouldCheckBirthdate {
        (isKrakenQuery && birthdateQuery) || isAwesomeQuery
    } else {
        isKrakenQuery || isAwesomeQuery
    }
}

我不知道你们,但SQL-style的IN运算符很难理解。PrediKit使这一部分更易于阅读人类代码

let awesomePeeps = ["Kraken", "Cthulhu", "Voldemort", "Ember", "Umber", "Voldemort"]
let predicate = NSPredicate(ManagedLegend.self) { includeIf in
    includeIf.string("title").matchesAnyValueIn(awesomePeeps)
}

PrediKit还支持子查询谓词

let predicate = NSPredicate(ManagedLegend.self) { includeIf in
    includeIf.string("title").equals("The Almighty Kraken") &&

    //Only include Krakens that have more than three hungry cerberus friends
    includeIf.collection("cerberusFriends").subquery(ManagedCerberus.self) {
        $0.bool("isHungry").isTrue()
        return .IncludeIfMatched(.Amount(.IsGreaterThan(3)))
    }
}

选择器扩展模式

我个人非常喜欢在PrediKit中使用选择器扩展模式的变种。这允许你在使用API时避免拼写错误的属性名。它还允许你随意重命名选择器属性。通过重命名,PrediKit使用的每个选择器实例都应给出编译器错误,这样你就可以一口气完成工作,并且可以放心地知道你没有遗漏任何属性名,而且在重构时没有遗漏任何属性名。

import Foundation

extension Selector {
    private enum Names: String {
        case title
        case birthdate
        case age
        case friends
        case isAwesome
        case isHungry
    }

    private init(_ name: Names) {
        self.init(name.rawValue)
    }

    static let title = Selector(.title)
    static let birthdate = Selector(.birthdate)
    static let age = Selector(.age)
    static let friends = Selector(.friends)
    static let isAwesome = Selector(.isAwesome)
    static let isHungry = Selector(.isHungry)
}

创建类似这样的选择器扩展后,PrediKit变得更加易于表达

//BEFORE
let predicate = NSPredicate(ManagedLegend.self) { includeIf in
    includeIf.string("title").equals("The Almighty Kraken")
}
//AFTER
let predicate = NSPredicate(ManagedLegend.self) { includeIf in
    includeIf.string(.title).equals("The Almighty Kraken")
}

许可证

PrediKit采用MIT许可证。查看LICENSE文件了解更多信息。