Skip to content

Commit 8d605b5

Browse files
StructuredQueries (#34)
* wip * Add overloads * wip * wip * wip * select only color for search query * Revert "select only color for search query" This reverts commit 0f7cf74. * wip * wip * wip * wip * clean up * more clean up * a little bit more clean up * more clean up * wip * disable indices for now * wip * wip * wip * wip * remove mutablerecord conformances * rename column * wip * wip * Convert SyncUps to StructuredQueries. * wip * wip * wip * fixes * SyncUpForm tests' * wip * wip * wip * wip * wip * wip * wip * fixes * wip * fix * wip * wip * wip * wip * wip * wip * wip; * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Use 'unscoped' branch from structured queries. * Update for unscoped branch. * clean up * wip * fix * wip * wip * wip * update migratinos * wip * wip * wip * wipo * wip * wip * wip * wip * clean up * wip * Clean uop * wip * wip * wip * clean up * wip * wip * format * wip * format * wip * batch seed * wip * clean up * wip * wip * wip * wip * wip * S Q -> SQ * rename name to title * docs * wip * wip * wip * wip * wip * wip * wip * wip * clean up * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Add fetchOne overloads for SelectStatement * Basic support for lists of reminders by tag * clean up * clean up * clean up * clean up * wip * wip * wip * clean up * Bump * wip * wip * fix * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip --------- Co-authored-by: Brandon Williams <[email protected]>
1 parent 5e38ad6 commit 8d605b5

File tree

79 files changed

+5435
-1795
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+5435
-1795
lines changed

.spi.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@ builder:
33
configs:
44
- documentation_targets:
55
- SharingGRDB
6-
swift_version: 6.0
6+
- SharingGRDBCore
7+
- StructuredQueriesGRDB
8+
- StructuredQueriesGRDBCore
9+
custom_documentation_parameters: [--enable-experimental-overloaded-symbol-presentation]

Examples/CaseStudies/Animations.swift

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Dependencies
21
import SharingGRDB
32
import SwiftUI
43

@@ -7,14 +6,14 @@ struct AnimationsCaseStudy: SwiftUICaseStudy {
76
This demonstrates how to animate fetching data from the database, or when data changes in \
87
the database. Simply provide the `animation` argument to `fetchAll` (or the other querying \
98
tools, such as `fetch` and `fetchOne`).
10-
9+
1110
This is analogous to how animations work in SwiftData in which one provides an `animation` \
1211
argument to the `@Query` macro.
1312
"""
1413
let caseStudyTitle = "Animations"
1514

16-
@SharedReader(.fetchAll(sql: #"SELECT * FROM "facts" ORDER BY "id" DESC"#, animation: .default))
17-
private var facts: [Fact]
15+
@FetchAll(Fact.order { $0.id.desc() }, animation: .default)
16+
private var facts
1817

1918
@Dependency(\.defaultDatabase) var database
2019

@@ -36,32 +35,35 @@ struct AnimationsCaseStudy: SwiftUICaseStudy {
3635
as: UTF8.self
3736
)
3837
try await database.write { db in
39-
_ = try Fact(body: fact).inserted(db)
38+
try Fact.insert(Fact.Draft(body: fact))
39+
.execute(db)
4040
}
4141
}
4242
} catch {}
4343
}
4444
}
4545
}
4646

47-
private struct Fact: Codable, FetchableRecord, Identifiable, MutablePersistableRecord {
48-
static let databaseTableName = "facts"
49-
var id: Int64?
47+
@Table
48+
private struct Fact: Identifiable {
49+
let id: Int
5050
var body: String
51-
mutating func didInsert(_ inserted: InsertionSuccess) {
52-
id = inserted.rowID
53-
}
5451
}
5552

5653
extension DatabaseWriter where Self == DatabaseQueue {
5754
static var animationDatabase: Self {
5855
let databaseQueue = try! DatabaseQueue()
5956
var migrator = DatabaseMigrator()
6057
migrator.registerMigration("Create 'facts' table") { db in
61-
try db.create(table: Fact.databaseTableName) { table in
62-
table.autoIncrementedPrimaryKey("id")
63-
table.column("body", .text).notNull()
64-
}
58+
try #sql(
59+
"""
60+
CREATE TABLE "facts" (
61+
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
62+
"body" TEXT NOT NULL
63+
)
64+
"""
65+
)
66+
.execute(db)
6567
}
6668
try! migrator.migrate(databaseQueue)
6769
return databaseQueue

Examples/CaseStudies/App.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ struct CaseStudiesApp: App {
66
var body: some Scene {
77
WindowGroup {
88
Form {
9-
Text("""
9+
Text(
10+
"""
1011
Open the preview in each case study file to run a case study.
11-
""")
12+
"""
13+
)
1214
}
1315
}
1416
}

Examples/CaseStudies/DynamicQuery.swift

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Dependencies
21
import SharingGRDB
32
import SwiftUI
43

@@ -8,13 +7,13 @@ struct DynamicQueryDemo: SwiftUICaseStudy {
87
a fact about a number is loaded from the network and saved to a database. You can search the \
98
facts for text, and the list will stay in sync so that if a new fact is added to the database \
109
that satisfies the search term, it will immediately appear.
11-
12-
To accomplish this one can invoke the `load` method defined on the `@SharedReader` projected \
13-
value in order to set a new query with dynamic parameters.
10+
11+
To accomplish this one can invoke the `load` method defined on the `@Fetch` projected value in \
12+
order to set a new query with dynamic parameters.
1413
"""
1514
let caseStudyTitle = "Dynamic Query"
1615

17-
@State.SharedReader(.fetch(Facts(), animation: .default)) private var facts = Facts.Value()
16+
@Fetch(Facts(), animation: .default) private var facts = Facts.Value()
1817
@State var query = ""
1918

2019
@Dependency(\.defaultDatabase) var database
@@ -38,10 +37,14 @@ struct DynamicQueryDemo: SwiftUICaseStudy {
3837
ForEach(facts.facts) { fact in
3938
Text(fact.body)
4039
}
41-
.onDelete { indexSet in
40+
.onDelete { indices in
4241
withErrorReporting {
4342
try database.write { db in
44-
_ = try Fact.deleteAll(db, ids: indexSet.compactMap { facts.facts[$0].id })
43+
let ids = indices.map { facts.facts[$0].id }
44+
try Fact
45+
.where { $0.id.in(ids) }
46+
.delete()
47+
.execute(db)
4548
}
4649
}
4750
}
@@ -50,7 +53,7 @@ struct DynamicQueryDemo: SwiftUICaseStudy {
5053
.searchable(text: $query)
5154
.task(id: query) {
5255
await withErrorReporting {
53-
try await $facts.load(.fetch(Facts(query: query), animation: .default))
56+
try await $facts.load(Facts(query: query), animation: .default)
5457
}
5558
}
5659
.task {
@@ -65,7 +68,8 @@ struct DynamicQueryDemo: SwiftUICaseStudy {
6568
as: UTF8.self
6669
)
6770
try await database.write { db in
68-
_ = try Fact(body: fact).inserted(db)
71+
try Fact.insert(Fact.Draft(body: fact))
72+
.execute(db)
6973
}
7074
}
7175
} catch {}
@@ -80,35 +84,39 @@ struct DynamicQueryDemo: SwiftUICaseStudy {
8084
var totalCount = 0
8185
}
8286
func fetch(_ db: Database) throws -> Value {
83-
let query = Fact.order(Column("id").desc).filter(Column("body").like("%\(query)%"))
87+
let search =
88+
Fact
89+
.where { $0.body.contains(query) }
90+
.order { $0.id.desc() }
8491
return try Value(
85-
facts: query.fetchAll(db),
86-
searchCount: query.fetchCount(db),
87-
totalCount: Fact.fetchCount(db)
92+
facts: search.fetchAll(db),
93+
searchCount: search.fetchCount(db),
94+
totalCount: Fact.all.fetchCount(db)
8895
)
8996
}
9097
}
91-
9298
}
9399

94-
private struct Fact: Codable, FetchableRecord, Identifiable, MutablePersistableRecord {
95-
static let databaseTableName = "facts"
96-
var id: Int64?
100+
@Table
101+
private struct Fact: Identifiable {
102+
let id: Int
97103
var body: String
98-
mutating func didInsert(_ inserted: InsertionSuccess) {
99-
id = inserted.rowID
100-
}
101104
}
102105

103106
extension DatabaseWriter where Self == DatabaseQueue {
104107
static var dynamicQueryDatabase: Self {
105108
let databaseQueue = try! DatabaseQueue()
106109
var migrator = DatabaseMigrator()
107110
migrator.registerMigration("Create 'facts' table") { db in
108-
try db.create(table: Fact.databaseTableName) { table in
109-
table.autoIncrementedPrimaryKey("id")
110-
table.column("body", .text).notNull()
111-
}
111+
try #sql(
112+
"""
113+
CREATE TABLE "facts" (
114+
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
115+
"body" TEXT NOT NULL
116+
)
117+
"""
118+
)
119+
.execute(db)
112120
}
113121
try! migrator.migrate(databaseQueue)
114122
return databaseQueue

Examples/CaseStudies/ObservableModelDemo.swift

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Dependencies
21
import SharingGRDB
32
import SwiftUI
43

@@ -7,7 +6,7 @@ struct ObservableModelDemo: SwiftUICaseStudy {
76
This demonstrates how to use the `fetchAll` and `fetchOne` tools in an @Observable model. \
87
In SwiftUI, the `@Query` macro only works when installed directly in a SwiftUI view, and \
98
cannot be used outside of views.
10-
9+
1110
The tools provided with this library work basically anywhere, including in `@Observable` \
1211
models and UIKit view controllers.
1312
"""
@@ -47,10 +46,10 @@ struct ObservableModelDemo: SwiftUICaseStudy {
4746
@MainActor
4847
private class Model {
4948
@ObservationIgnored
50-
@SharedReader(.fetchAll(sql: #"SELECT * FROM "facts" ORDER BY "id" DESC"#, animation: .default))
51-
var facts: [Fact]
49+
@FetchAll(Fact.order { $0.id.desc() }, animation: .default)
50+
var facts
5251
@ObservationIgnored
53-
@SharedReader(.fetchOne(sql: #"SELECT count(*) FROM "facts""#, animation: .default))
52+
@FetchOne(Fact.count(), animation: .default)
5453
var factsCount = 0
5554
var number = 0
5655

@@ -66,38 +65,45 @@ private class Model {
6665
as: UTF8.self
6766
)
6867
try await database.write { db in
69-
_ = try Fact(body: fact).inserted(db)
68+
try Fact.insert(Fact.Draft(body: fact))
69+
.execute(db)
7070
}
7171
}
7272
}
7373

7474
func deleteFact(indices: IndexSet) {
75-
_ = withErrorReporting {
75+
withErrorReporting {
7676
try database.write { db in
77-
try Fact.deleteAll(db, ids: indices.compactMap { facts[$0].id })
77+
let ids = indices.map { facts[$0].id }
78+
try Fact
79+
.where { $0.id.in(ids) }
80+
.delete()
81+
.execute(db)
7882
}
7983
}
8084
}
8185
}
8286

83-
private struct Fact: Codable, FetchableRecord, Identifiable, MutablePersistableRecord {
84-
static let databaseTableName = "facts"
85-
var id: Int64?
87+
@Table
88+
private struct Fact: Identifiable {
89+
let id: Int
8690
var body: String
87-
mutating func didInsert(_ inserted: InsertionSuccess) {
88-
id = inserted.rowID
89-
}
9091
}
9192

9293
extension DatabaseWriter where Self == DatabaseQueue {
9394
static var observableModelDatabase: Self {
9495
let databaseQueue = try! DatabaseQueue()
9596
var migrator = DatabaseMigrator()
9697
migrator.registerMigration("Create 'facts' table") { db in
97-
try db.create(table: Fact.databaseTableName) { table in
98-
table.autoIncrementedPrimaryKey("id")
99-
table.column("body", .text).notNull()
100-
}
98+
try #sql(
99+
"""
100+
CREATE TABLE "facts" (
101+
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
102+
"body" TEXT NOT NULL
103+
)
104+
"""
105+
)
106+
.execute(db)
101107
}
102108
try! migrator.migrate(databaseQueue)
103109
return databaseQueue

Examples/CaseStudies/SwiftDataTemplateDemo.swift

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ struct SwiftDataTemplateView: SwiftUICaseStudy {
99
let caseStudyTitle = "SwiftData Template"
1010

1111
@Dependency(\.defaultDatabase) private var database
12-
@SharedReader(.fetch(Items(), animation: .default)) private var items
12+
@FetchAll(Item.all, animation: .default) private var items
1313

1414
var body: some View {
1515
NavigationStack {
1616
List {
17-
ForEach(items, id: \.id) { item in
17+
ForEach(items) { item in
1818
NavigationLink {
1919
Text(
2020
"Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))"
@@ -41,28 +41,24 @@ struct SwiftDataTemplateView: SwiftUICaseStudy {
4141
private func addItem() {
4242
withErrorReporting {
4343
try database.write { db in
44-
_ = try Item(timestamp: Date()).inserted(db)
44+
try Item.insert().execute(db)
4545
}
4646
}
4747
}
4848

4949
private func deleteItems(offsets: IndexSet) {
5050
withErrorReporting {
5151
try database.write { db in
52-
_ = try Item.deleteAll(db, keys: offsets.map { items[$0].id })
52+
try Item.where { $0.id.in(offsets.map { items[$0].id }) }.delete().execute(db)
5353
}
5454
}
5555
}
56-
57-
private struct Items: FetchKeyRequest {
58-
func fetch(_ db: Database) throws -> [Item] {
59-
try Item.order(Column("timestamp").desc).fetchAll(db)
60-
}
61-
}
6256
}
6357

64-
private struct Item: Codable, Hashable, FetchableRecord, MutablePersistableRecord {
65-
var id: Int64?
58+
@Table
59+
private struct Item: Identifiable {
60+
let id: Int
61+
@Column(as: Date.ISO8601Representation.self)
6662
var timestamp: Date
6763
}
6864

@@ -71,10 +67,15 @@ extension DatabaseWriter where Self == DatabaseQueue {
7167
let databaseQueue = try! DatabaseQueue()
7268
var migrator = DatabaseMigrator()
7369
migrator.registerMigration("Create items table") { db in
74-
try db.create(table: Item.databaseTableName) { table in
75-
table.autoIncrementedPrimaryKey("id")
76-
table.column("timestamp", .datetime).notNull()
77-
}
70+
try #sql(
71+
"""
72+
CREATE TABLE "items" (
73+
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
74+
"timestamp" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
75+
)
76+
"""
77+
)
78+
.execute(db)
7879
}
7980
try! migrator.migrate(databaseQueue)
8081
return databaseQueue

0 commit comments

Comments
 (0)