CoreDataQueryInterface 6.1

CoreDataQueryInterface 6.1

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布最新发布2018年4月
SwiftSwift版本4.1
SPM支持SPM

Gregory Higley维护。



CoreDataQueryInterface

CocoaPods compatible Carthage compatible Language Platforms

Core Data查询接口(CDQI)是一个类型安全、流畅、直观的库,用于在Swift中操作Core Data。CDQI将执行Core Data所需代码的数量大幅减少,并通过允许方法链和消除魔法字符串,极大地改善了代码的可读性。CDQI有点像jQuery或LINQ,但针对Core Data。

特性

  • 流畅接口,即链式方法
  • 大量有用的重载
  • 过滤器比较中的类型安全
  • 过滤、排序、分组、聚合表达式、限制等
  • 可选地消除了在Core Data中常用到的魔法字符串
  • 查询重用,即链式调用无副作用
  • 支持iOS 9+、macOS 10.11+、tvOS 9+和watchOS 2+
  • Swift 4

概述

基本上,CDQI是一个允许使用流畅语法创建(和执行)fetch请求的工具。在大多数情况下,这将许多行代码减少为单行(但仍然高度可读)。

let swiftDevelopers = managedObjectContext.from(Developer.self).
                      filter{ any($0.languages.name == "Swift") }.
                      order(ascending: false, {$0.lastName})
                      .limit(5)
                      .all()

集成

Carthage

在您的 Cartfile 文件中,添加以下行

github "prosumma/CoreDataQueryInterface" ~> 6.0

CocoaPods

将以下内容添加到您的 Podfile 中。如果之前没有添加的话,您还需要添加 use_frameworks!

pod 'CoreDataQueryInterface', '~> 6.0'

属性代理

为了使用如上示例中的 $0.languages.name 这样的表达式,必须创建代理对象。在项目根目录的 bin 文件夹中有一个名为 cdqi 的简单工具,可以完成这项任务。运行此工具之前,请确保每个 NSManagedObject 在您的 Swift 项目中都有一个对应的类。

cdqi Developers

它将递归地搜索所有子目录,直到找到一个名为 Developers.xcdatamodeld 的托管对象模型。然后检查该模型的 当前版本 并为每个 NSManagedObject 生成代理类。默认情况下,这些代理类放在托管对象模型相同的目录中,并与模型并排放置。cdqi 提供了许多选项来更改此行为,但在大多数情况下,默认选项则是什么您想要的。要获取更多选项,请执行 cdqi --help

请注意,当使用自动生成的 Core Data 类时,您应该使用 --public 标志创建代理,否则您将遇到编译错误。

类型安全

CDQI 支持过滤器表达式的类型安全。在表达式 $0.languages.name 中,name 属性已在 Core Data 模型中定义为字符串,因此它只能与字符串进行比较。以下将无法编译

$0.languages.name == 4

为了支持扩展性,CDQI 的类型安全实际上比上述描述更为复杂。Swift 的 String 类型能够参与字符串属性的比较,因为它实现了 TypedExpressionConvertible

extension String: TypedExpressionConvertible {
    public typealias CDQIComparisonType = String
    public static let cdqiStaticType = NSAttributeType.stringAttributeType
    public var cdqiExpression: NSExpression {
        return NSExpression(forConstantValue: self)
    }
}

通过实现 TypedExpressionConvertible 协议,并将它的 CDQIComparisonType 作为 String 的同义词 typealias 定义,可以为类型实现 CDQI 字符串比较。要参与数值比较,则需要将 CDQIComparisonType 定义为 NSNumber

设想有一个 Weekday 枚举,我们希望将 Core Data 的一个 Int32 属性与其进行比较。而不是使用代码 $0.weekday == Weekday.Monday.rawValue,我们可以使其更加简洁。

public enum Weekday: Int {
    case Sunday = 1
    case Monday = 2
    case Tuesday = 3
    case Wednesday = 4
    case Thursday = 5
    case Friday = 6
    case Saturday = 7    
}

extension Weekday: TypedExpressionConvertible {
    public typealias CDQIComparisonType = NSNumber
    public static let cdqiStaticType = NSAttributeType.integer32AttributeType
    public var cdqiExpression: NSExpression {
        return NSExpression(forConstantValue: NSNumber(value: rawValue))
    }
}

现在我们可以说 $0.weekday == Weekday.Monday。任何类型都可以使用此技术参与 CDQI 过滤比较。

查询重用

CDQI 尽可能在所有地方使用值类型。大多数 CDQI 方法,如 filterorder 等,返回值类型 Query<M, R>。这使得以下技术变得可能:

let projectQuery = Query<Project, Project>()
let swiftProjectQuery = projectQuery.filter{ any($0.languages.name == "Swift") }

第二条语句对第一条语句没有副作用。

示例

Examples 文件夹的单元测试和“Top Hits”示例应用中可以找到大量示例,但这里简要展示几个。

let developerQuery = managedObjectContext.from(Developer.self)
// predicate: languages.@count == 3 AND ANY languages.name == "Rust"
// developersWhoKnowThreeLanguagesIncludingRust is an array of Developer entities
let developersWhoKnowThreeLanguagesIncludingRust = developerQuery.filter{ $0.languages.cdqiCount() == 3 &&
                                                   any($0.languages.name == "Rust") }.all()

// predicate: ANY languages.name == "Haskell"
// haskellDevelopers is an array of dictionaries, e.g., [["firstName": "Haskell", "lastName": "Curry"]]
let haskellDevelopers = developerQuery.
                        filter{ developer in any(developer.languages.name == "Haskell") }.
                        select{ developer in [developer.firstName, developer.lastName] }.all()

// Instead of using $0, we can create a proxy up front.
let project = Project.CDQIAttribute()

// We can do a query in a single line
var swiftProjectNames: [String] = managedObjectContext.from(Project.self).
                                  filter(any(project.languages.name == "Swift")).
                                  order(project.name).array(project.name)

// Or we can build it up in multiple lines
var projectQuery = managedObjectContext.from(Project.self)
projectQuery = projectQuery.filter(any(project.languages.name == "Swift"))
projectQuery = projectQuery.order(project.name)
swiftProjectNames = projectQuery.array(project.name)