11# swift-query
2- A simple query language for Swift Data with automatic support for Swift concurrency.
2+ A simple query language for SwiftData with automatic support for Swift concurrency.
33
44## Usage
55
66``` swift
7- // Main context
7+ // Query from the main context
88let people = Query< Person> ()
99 .include (#Predicate { $0 .age >= 18 } )
1010 .sortBy (\.age )
1111 .results (in : modelContainer)
12+ for person in people {
13+ print (" Adult: \( person.name ) , age \( person.age ) " )
14+ }
15+
1216
1317// Or a background context
1418Task.detached {
15- let people = await modelContainer.perform {
16- Query< Person> ()
19+ await modelContainer.queryActor (). perform { _ in
20+ let people = Query< Person> ()
1721 .include (#Predicate { $0 .age >= 18 } )
1822 .sortBy (\.age )
1923 .results ()
24+
25+ for person in people {
26+ person.age += 1
27+ }
2028 }
2129}
2230```
2331
2432### Building Queries
2533
26- Queries are an expressive layer on top of Swift Data that allow us to quickly build
34+ Queries are an expressive layer on top of SwiftData that allow us to quickly build
2735complex fetch decriptors by successively applying refinements. The resulting query can
2836be saved for reuse or performed immediately.
2937
@@ -52,6 +60,24 @@ Person.include(#Predicate { $0.name == "Jack" })
5260Person.exclude (#Predicate { $0 .age > 25 })
5361```
5462
63+ #### Creating compound refinements
64+
65+ Multiple ` include() ` and ` exclude() ` calls create compound predicates using AND logic, allowing you to build complex filters:
66+
67+ ``` swift
68+ // Find adult Jacks who are active
69+ Person.include (#Predicate { $0 .age >= 18 })
70+ .include (#Predicate { $0 .name == " Jack" })
71+ .exclude (#Predicate { $0 .isInactive })
72+ ```
73+
74+ This creates a compound predicate equivalent to:
75+ ``` swift
76+ #Predicate < Person> { person in
77+ person.age >= 18 && person.name == " Jack" && ! person.isInactive
78+ }
79+ ```
80+
5581#### Ordering results
5682
5783Queries allow their results to be ordered:
@@ -122,7 +148,7 @@ we pass in our model container and SwiftQuery will use the container's main cont
122148
123149#### Fetching one result
124150
125- Often we just want
151+ Often we just want to fetch a single result.
126152
127153``` swift
128154let jillQuery = Person.include (#Predicate { $0 .name == " Jill" })
@@ -153,7 +179,7 @@ let lazyAdults = Person
153179
154180#### Fetching or creating objects matching a query
155181
156- A common pattern in Core Data (and so in Swift Data ), is to want to fetch an object
182+ A common pattern in Core Data (and so in SwiftData ), is to want to fetch an object
157183based on a set of filters, or create a new one by default in the case that object
158184does not yet exist. This is easy with SwiftQuery using ` findOrCreate ` :
159185
@@ -163,7 +189,6 @@ let jill = Person
163189 .findOrCreate (in : container) {
164190 Person (name : " Jill" )
165191 }
166- }
167192```
168193
169194### Performing queries in a concurrent context
@@ -188,31 +213,36 @@ actor MyActor {
188213}
189214```
190215
191- We also expose an async ` perform ` function on ` ModelContainer ` that allow you to
192- implicitly use SwiftQuery's ` QueryActor ` to run queries:
216+ We also expose an async ` perform ` functions on a SwiftQuery's default actor that allow you to
217+ implicitly use ` QueryActor ` to run queries:
193218
194219``` swift
195- let allJills = try await modelContainer.perform {
196- Person
220+ await modelContainer.queryActor (). perform { _ in
221+ let allJills = Person
197222 .include (#Predicate { $0 .name == " Jill" })
198223 .results ()
224+
225+ // Process Jills within the actor context
226+ for jill in allJills {
227+ print (" Found Jill: \( jill.name ) " )
228+ }
199229}
200230```
201231
202- You can of course pass this (or any) model actor explicitly as the isolation context :
232+ Or, to return a value :
203233
204234``` swift
205- Task {
206- let actor = QueryActor (modelContainer : myModelContainer)
207- let allJills = try await Person
208- .include (#Predicate { $0 .name == " Jill" })
209- .results (isolation : actor)
210- }
235+ let count = await modelContainer.queryActor ().perform { _ in
236+ Query< Person> ()
237+ .include (#Predicate { $0 .age >= 18 } )
238+ .count ()
239+ }
211240```
212241
213-
214- ## TODO
215- - Tests
242+ Note that models cannot be returned out of the actor's isolation context using this function;
243+ only ` Sendable ` values can be transported across the boundary. This means the compiler
244+ effectively makes it impossible to use the models incorrectly in a multi-threaded context,
245+ thus guaranteeing the SwiftData concurrency contract at compile time.
216246
217247## Installation
218248
@@ -232,7 +262,7 @@ dependencies: [
232262And then adding the product to any target that needs access to the library:
233263
234264``` swift
235- .product (name : " SwiftQuery" , package : " swift-squery " ),
265+ .product (name : " SwiftQuery" , package : " swift-query " ),
236266```
237267
238268## License
0 commit comments