Skip to content

Commit 8ddd11f

Browse files
committed
README
1 parent 5663ea3 commit 8ddd11f

File tree

1 file changed

+132
-7
lines changed

1 file changed

+132
-7
lines changed

README.md

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,138 @@
22

33
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.
44

5-
### Swift 3
5+
### Features
66

7-
This is a placeholder for the Swift 3 documentation for CDQI v5. CDQI v5 for Swift 3 is **usable right now** with some caveats:
7+
- [x] [Fluent interface](http://en.wikipedia.org/wiki/Fluent_interface), i.e., chainable methods
8+
- [x] Large number of useful overloads
9+
- [x] Type-safety in filter comparisons.
10+
- [x] Filtering, sorting, grouping, aggregate expressions, limits, etc.
11+
- [x] Optionally eliminates the use of magic strings so common in Core Data
12+
- [x] Query reuse, i.e., no side-effects from chaining
13+
- [x] Support for iOS 9+, macOS 10.11+, tvOS 9+, and watchOS 2+.
814

9-
- iOS 9.0+, macOS 10.11+, tvOS 9.0+ and watchOS 2.0+ are supported.
10-
- There is **absolutely no** documentation, and things have changed. The unit tests are your friends. Look at them. Documentation—both here and inline—is coming.
11-
- Because all of the unit tests pass, it is highly likely that the API will remain stable, but small tweaks are possible, particularly when it comes to Swift 3's new access modifiers.
12-
- Core Data inheritance is still not supported. I currently have no plans to do so, but there are workarounds.
15+
### Overview
1316

14-
As always, if you find a bug or have a feature request, open an issue.
17+
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.
18+
19+
```swift
20+
let swiftDevelopers = managedObjectContext.from(Developer).
21+
filter{ any($0.languages.name == "Swift") }.
22+
order(descending: {$0.lastName}).limit(5).all()
23+
```
24+
25+
### Integration
26+
27+
#### Carthage
28+
29+
In your `Cartfile`, add the following line:
30+
31+
```ruby
32+
github "prosumma/CoreDataQueryInterface" ~> 5.0
33+
```
34+
35+
#### CocoaPods
36+
37+
Add the following to your `Podfile`. If it isn't already present, you will have to add `use_frameworks!` as well.
38+
39+
```ruby
40+
pod 'CoreDataQueryInterface', '~> 5.0'
41+
```
42+
43+
#### Attribute Proxies
44+
45+
In order to use expressions such as `$0.languages.name` as in the example above, proxy objects must be created. In the `bin` folder at the root of the project is a simple tool called `cdqi` that accomplishes this. Before running this tool, make sure that each `NSManagedObject` is represented by a corresponding class in your Swift project.
46+
47+
```sh
48+
cdqi Developers
49+
```
50+
51+
This searches all subdirectories recursively until it finds a managed object model called `Developers.xcdatamodeld`. It then examines the _current version_ of this model and generates proxy classes for each `NSManagedObject`. By default, these proxy classes are placed in the same directory as the managed object model, side by side. `cdqi` has many options to change this behavior if desired, but in most cases the default is what you want. For more options, execute `cdqi --help`.
52+
53+
### Type Safety
54+
55+
CDQI supports type safety in filter expressions. In the expression `$0.languages.name`, the `name` attribute has been defined as a string in the Core Data model, so it can only be compared to strings. The following will not compile:
56+
57+
```swift
58+
$0.languages.name == 4
59+
```
60+
61+
In order to support extensibility, CDQI's type safety is actually more sophisticated than described above. The Swift `String` type is able to participate in comparisons to string attributes because it implements `TypedExpressionConvertible`:
62+
63+
```swift
64+
extension String: TypedExpressionConvertible {
65+
public typealias CDQIComparisonType = String
66+
public static let cdqiStaticType = NSAttributeType.stringAttributeType
67+
public var cdqiExpression: NSExpression {
68+
return NSExpression(forConstantValue: self)
69+
}
70+
}
71+
```
72+
73+
By implementing the `TypedExpressionConvertible` protocol and defining its `CDQIComparisonType` `typealias` as `String`, a type can be made to participate in CDQI string comparisons. To participate in numeric comparisons, `CDQIComparisonType` should be `NSNumber`.
74+
75+
Imagine a `Weekday` enumeration to which we wish to compare an `Int32` Core Data attribute. Instead of saying `$0.weekday == Weekday.Monday.rawValue`, we can make things a little nicer:
76+
77+
```swift
78+
public enum Weekday: Int {
79+
case Sunday = 1
80+
case Monday = 2
81+
case Tuesday = 3
82+
case Wednesday = 4
83+
case Thursday = 5
84+
case Friday = 6
85+
case Saturday = 7
86+
}
87+
88+
extension Weekday: TypedExpressionConvertible {
89+
public typealias CDQIComparisonType = NSNumber
90+
public static let cdqiStaticType = NSAttributeType.integer32AttributeType
91+
public var cdqiExpression: NSExpression {
92+
return NSExpression(forConstantValue: NSNumber(value: rawValue))
93+
}
94+
}
95+
```
96+
97+
Now we can say `$0.weekday == Weekday.Monday`. Any type can be made to participate in CDQI filter comparisons using this technique.
98+
99+
### Query Reuse
100+
101+
CDQI uses value types wherever possible. Most CDQI methods such as `filter`, `order`, and so on return value types such as `EntityQuery` or `ExpressionQuery`. This allows techniques such as the following:
102+
103+
```swift
104+
let projectQuery = Query<Project, Project>.from(Project.self)
105+
let swiftProjectQuery = projectQuery.filter{ any($0.languages.name == "Swift") }
106+
```
107+
108+
The second statement causes no side effects on the first one.
109+
110+
### Examples
111+
112+
A great number of examples can be found in the unit tests and the "Top Hits" example app in the `Examples` folder, but here are a few at a glance.
113+
114+
```swift
115+
let developerQuery = managedObjectContext.from(Developer.self)
116+
// predicate: languages.@count == 3 AND ANY languages.name == "Rust"
117+
// developersWhoKnowThreeLanguagesIncludingRust is an array of Developer entities
118+
let developersWhoKnowThreeLanguagesIncludingRust = developerQuery.filter{ $0.languages.count == 3 &&
119+
any($0.languages.name == "Rust") }.all()
120+
121+
// predicate: ANY languages.name == "Haskell"
122+
// haskellDevelopers is an array of dictionaries, e.g., [["firstName": "Haskell", "lastName": "Curry"]]
123+
let haskellDevelopers = developerQuery.
124+
filter{ developer in any(developer.languages.name == "Haskell") }.
125+
select{ developer in [developer.firstName, developer.lastName] }.all()
126+
127+
// Instead of using $0, we can create a proxy up front.
128+
let project = Project.CDQIAttribute()
129+
130+
// We can do a query in a single line
131+
var swiftProjectNames: [String] = managedObjectContext.from(Project.self).
132+
filter(any(project.languages.name == "Swift")).
133+
order(project.name).array(project.name)
134+
135+
// Or we can build it up in multiple lines
136+
var projectQuery = managedObjectContext.from(Project)
137+
projectQuery = projectQuery.filter(any(project.languages.name == "Swift"))
138+
projectQuery = projectQuery.order(project.name)
139+
swiftProjectNames = projectQuery.array(project.name)

0 commit comments

Comments
 (0)