Skip to content

Commit 357d2d4

Browse files
committed
README
1 parent bb394bd commit 357d2d4

File tree

1 file changed

+29
-102
lines changed

1 file changed

+29
-102
lines changed

README.md

Lines changed: 29 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,59 @@
11
![CoreDataQueryInterface](CoreDataQueryInterface.png)
22

3-
[![CocoaPods compatible](https://img.shields.io/cocoapods/v/CoreDataQueryInterface.svg)](https://cocoapods.org)
4-
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
5-
[![Language](https://img.shields.io/badge/Swift-5.1-orange.svg)](http://swift.org)
6-
![Platforms](https://img.shields.io/cocoapods/p/CoreDataQueryInterface.svg)
3+
[![Language](https://img.shields.io/badge/Swift-5.7-blue.svg)](http://swift.org)
74

8-
Core Data Query Interface (CDQI) is a type-safe, fluent, intuitive library for working with Core Data in Swift. CDQI tremendously reduces the amount of code needed to do Core Data, and dramatically improves readability by allowing method chaining and by eliminating magic strings. CDQI is a bit like jQuery or LINQ, but for Core Data.
5+
Core Data Query Interface (CDQI) is a type-safe, fluent, intuitive library for working with Core Data in Swift. CDQI tremendously reduces the amount of code needed to do Core Data, and dramatically improves readability and refactoring by allowing method chaining and by eliminating magic strings.
96

10-
NOTE: The `cdqi` tool used to generate attribute proxies is deprecated. Bug fixes and changes in Swift make it very simple to hand-code attribute proxies, so the `cdqi` tool is no longer necessary.
7+
CDQI uses the [PredicateQI](https://github.com/prosumma/PredicateQI) (PQI) package. PQI provides a type-safe Swift interface on top of Apple's `NSPredicate` language. CDQI adds the machinery needed to make PQI work with Core Data.
118

12-
### Features
9+
## Features
1310

1411
- [x] [Fluent interface](http://en.wikipedia.org/wiki/Fluent_interface), i.e., chainable methods
1512
- [x] Large number of useful overloads
1613
- [x] Type-safety in filter comparisons.
1714
- [x] Filtering, sorting, grouping, aggregate expressions, limits, etc.
1815
- [x] Optionally eliminates the use of magic strings so common in Core Data
1916
- [x] Query reuse, i.e., no side-effects from chaining
20-
- [x] Support for iOS 9+, macOS 10.11+, tvOS 9+, and watchOS 2+.
21-
- [x] Swift 5
17+
- [x] TODO: minimum OS support
18+
- [x] Swift 5.7
2219

23-
### Overview
20+
## Overview
2421

25-
In essence, CDQI is a tool that allows the creation (and execution) of fetch requests using a fluent syntax. In most cases, this can reduce many lines of code to a single (but still highly readable) line.
22+
The best way to understand how to use CDQI is to look at the unit tests, but an example will make things clear.
2623

27-
```swift
28-
let swiftDevelopers = managedObjectContext.from(Developer.self).
29-
filter(any(Developer.e.languages.name == "Swift"))
30-
orderDesc(by: Developer.e.lastName)
31-
.limit(5)
32-
.all()
33-
```
34-
35-
### Integration
36-
37-
#### Carthage
38-
39-
In your `Cartfile`, add the following line:
40-
41-
```ruby
42-
github "prosumma/CoreDataQueryInterface" ~> 7.0
43-
```
44-
45-
#### CocoaPods
46-
47-
Add the following to your `Podfile`. If it isn't already present, you will have to add `use_frameworks!` as well.
48-
49-
```ruby
50-
pod 'CoreDataQueryInterface', '~> 7.0'
51-
```
52-
53-
### Attribute And Model Proxies
54-
55-
CDQI works through the use of _attribute and model proxies_. In CDQI, a proxy is a type that stands in for a Core Data model or attribute. There are built-in proxies for all the Core Data attribute types, e.g., `Int32Attribute`, `StringAttribute` and so on. For your own Core Data models, you will need to create your own proxies, which is very simple to do. Imagine we have two Core Data models, `Employee` and `Department`. There is a many-to-one relationship in Core Data between these models. To keep things simple, each has a simple `name` attribute of type `String`:
24+
First, let's start with the following data model:
5625

5726
```swift
58-
class Employee: NSManagedObjectModel {
59-
@NSManaged var name: String
60-
@NSManaged var department: Department
27+
class Language: NSManagedObject {
28+
@NSManaged var name: String
29+
@NSManaged var developers: Set<Developer>
6130
}
6231

63-
class Department: NSManagedObjectModel {
64-
@NSManaged var name: String
65-
@NSManaged var employees: Set<Employee>
32+
class Developer: NSManagedObject {
33+
@NSManaged var firstName: String
34+
@NSManaged var lastName: String
35+
@NSManaged var languages: Set<Language>
6636
}
6737
```
6838

69-
The proxy classes for these should look like this:
39+
Given this data model, we can start making some queries:
7040

7141
```swift
72-
class EmployeeAttribute: EntityAttribute, Subqueryable {
73-
public private(set) lazy var name = StringAttribute(key: "name", parent: self)
74-
public private(set) lazy var department = DepartmentAttribute(key: "department", parent: self)
75-
}
42+
// Which languages are known by at least two of the developers?
43+
// developers.@count >= 2
44+
Query(Language.self).filter { $0.developers.pqiCount >= 2 }
7645

77-
extension Employee: Entity {
78-
public typealias CDQIEntityAttribute = EmployeeAttribute
79-
}
80-
81-
class DepartmentAttribute: EntityAttribute, Subqueryable {
82-
public private(set) lazy var name = StringAttribute(key: "name", parent: self)
83-
public private(set) lazy var employees: EmployeeAttribute(key: "employees", parent: self)
84-
}
85-
86-
extension Department: Entity {
87-
public typealias CDQIEntityAttribute = DepartmentAttribute
88-
}
46+
// Which languages are known by developers whose name contains 's'?
47+
// ANY developers.lastname LIKE[c] '*s*'
48+
Query(Language.self).filter { any(ci($0.developers.lastName %* "*s*")) }
8949
```
9050

91-
Once this is done, CDQI can do its magic. Read on.
92-
93-
### Starting a Query
94-
95-
A CDQI query is a chain of methods that build an `NSFetchRequest`. Almost all of the `NSFetchRequest`'s functionality is supported, such as choosing the result type, limiting the number of records fetched, filtering, sorting, etc.
96-
97-
A query is started by creating an instance of `Query`, which takes two generic type parameters. The first one tells us which `NSManagedObject` subclass is the target of our query. The second tells us what the result of the query should be: Either the same `NSManagedObject` subclass or an `NSDictionary`.
98-
99-
```swift
100-
let developerQuery = Query<Developer, Developer>()
101-
let developerDictionaryQuery = Query<Developer, NSDictionary>()
102-
```
103-
104-
Most `Query` instances are of the form `Query<M, M>` where `M` is an `NSManagedObject` which implements the `Entity` protocol. A perhaps better way to start a query is…
105-
106-
```swift
107-
let developerQuery = Developer.cdqiQuery
108-
```
109-
110-
Queries started with `Query<Developer, Developer>` or `Developer.cdqiQuery` have no implicit `NSManagedObjectContext`, so one must be passed when executing a query.
51+
We can get the `NSFetchRequest` produced by the query by asking for its `fetchRequest` property. But it's usually easier just to execute the fetch request directly:
11152

11253
```swift
113-
try Developer.cdqiQuery.order(by: Developer.e.lastName).all(managedObjectContext: moc)
114-
try Developer.cdqiQuery,order(by: Developer.e.lastName).context(moc).all()
54+
let rustaceans = try Query(Developer.self)
55+
.filter {
56+
any($0.languages.name == "Rust") && $0.experience >= 3
57+
}
58+
.fetch(moc)
11559
```
116-
117-
This pattern is so common that a convenience method exists on `NSManagedObjectContext`.
118-
119-
```swift
120-
try moc.from(Developer.self).order(by: Developer.e.lastName).all()
121-
```
122-
123-
### Filtering
124-
125-
Filtering in Core Data requires an `NSPredicate`. CDQI has overloads of many of the built-in operators. These overloads generate Core Data friendly `NSPredicate`s instead of `Bool`s. They are carefully designed so as not to conflict with the ordinary operators.
126-
127-
| Swift | `NSPredicate` |
128-
| --- | --- |
129-
| `Developer.e.lastName == "Li"` | `"lastName == 'Li'"` |
130-
| `Person.e.age >= 18` | `"age >= 18"` |
131-
| `21...55 ~= Person.e.age` | `"age BETWEEN 21 AND 55"` |
132-
| `Person.e.firstName == "Friedrich" && Person.e.lastName == "Hayek"` | `"firstName == 'Friedrich' AND lastName == 'Hayek'"` |

0 commit comments

Comments
 (0)