Skip to content

Commit 4301166

Browse files
committed
v0.1.0
0 parents  commit 4301166

33 files changed

+2076
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
xcuserdata/
3+
.swiftpm
4+
.build/

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Release Notes
2+
3+
## v0.1.0
4+
5+
Released on 2012/11/25

Documentation/QueryDemo/QueryDemo.xcodeproj/project.pbxproj

+751
Large diffs are not rendered by default.

Documentation/QueryDemo/QueryDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"object": {
3+
"pins": [
4+
{
5+
"package": "GRDB",
6+
"repositoryURL": "https://github.com/groue/GRDB.swift.git",
7+
"state": {
8+
"branch": null,
9+
"revision": "8658b7bc1009237457324fc050cd06e0d961b72a",
10+
"version": "5.13.0"
11+
}
12+
}
13+
]
14+
},
15+
"version": 1
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "iphone",
5+
"scale" : "2x",
6+
"size" : "20x20"
7+
},
8+
{
9+
"idiom" : "iphone",
10+
"scale" : "3x",
11+
"size" : "20x20"
12+
},
13+
{
14+
"idiom" : "iphone",
15+
"scale" : "2x",
16+
"size" : "29x29"
17+
},
18+
{
19+
"idiom" : "iphone",
20+
"scale" : "3x",
21+
"size" : "29x29"
22+
},
23+
{
24+
"idiom" : "iphone",
25+
"scale" : "2x",
26+
"size" : "40x40"
27+
},
28+
{
29+
"idiom" : "iphone",
30+
"scale" : "3x",
31+
"size" : "40x40"
32+
},
33+
{
34+
"idiom" : "iphone",
35+
"scale" : "2x",
36+
"size" : "60x60"
37+
},
38+
{
39+
"idiom" : "iphone",
40+
"scale" : "3x",
41+
"size" : "60x60"
42+
},
43+
{
44+
"idiom" : "ipad",
45+
"scale" : "1x",
46+
"size" : "20x20"
47+
},
48+
{
49+
"idiom" : "ipad",
50+
"scale" : "2x",
51+
"size" : "20x20"
52+
},
53+
{
54+
"idiom" : "ipad",
55+
"scale" : "1x",
56+
"size" : "29x29"
57+
},
58+
{
59+
"idiom" : "ipad",
60+
"scale" : "2x",
61+
"size" : "29x29"
62+
},
63+
{
64+
"idiom" : "ipad",
65+
"scale" : "1x",
66+
"size" : "40x40"
67+
},
68+
{
69+
"idiom" : "ipad",
70+
"scale" : "2x",
71+
"size" : "40x40"
72+
},
73+
{
74+
"idiom" : "ipad",
75+
"scale" : "1x",
76+
"size" : "76x76"
77+
},
78+
{
79+
"idiom" : "ipad",
80+
"scale" : "2x",
81+
"size" : "76x76"
82+
},
83+
{
84+
"idiom" : "ipad",
85+
"scale" : "2x",
86+
"size" : "83.5x83.5"
87+
},
88+
{
89+
"idiom" : "ios-marketing",
90+
"scale" : "1x",
91+
"size" : "1024x1024"
92+
}
93+
],
94+
"info" : {
95+
"author" : "xcode",
96+
"version" : 1
97+
}
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import GRDB
2+
3+
struct Player: Codable, Identifiable {
4+
var id: Int64?
5+
var name: String
6+
var score: Int
7+
var photoID: Int
8+
}
9+
10+
extension Player {
11+
private static let names = [
12+
"Arthur", "Anita", "Barbara", "Bernard", "Craig", "Chiara", "David",
13+
"Dean", "Éric", "Elena", "Fatima", "Frederik", "Gilbert", "Georgette",
14+
"Henriette", "Hassan", "Ignacio", "Irene", "Julie", "Jack", "Karl",
15+
"Kristel", "Louis", "Liz", "Masashi", "Mary", "Noam", "Nicole",
16+
"Ophelie", "Oleg", "Pascal", "Patricia", "Quentin", "Quinn", "Raoul",
17+
"Rachel", "Stephan", "Susie", "Tristan", "Tatiana", "Ursule", "Urbain",
18+
"Victor", "Violette", "Wilfried", "Wilhelmina", "Yvon", "Yann",
19+
"Zazie", "Zoé"]
20+
21+
/// Creates a new player with random name and random score
22+
static func makeRandom(id: ID = nil) -> Player {
23+
Player(
24+
id: id,
25+
name: names.randomElement()!,
26+
score: 10 * Int.random(in: 0...100),
27+
photoID: Int.random(in: 0...1000))
28+
}
29+
30+
/// A placeholder Player
31+
static let placeholder = Player(name: "xxxxxx", score: 100, photoID: 1)
32+
}
33+
34+
extension Player: FetchableRecord, MutablePersistableRecord {
35+
mutating func didInsert(with rowID: Int64, for column: String?) {
36+
id = rowID
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import GRDB
2+
3+
/// Returns an empty in-memory database for the application.
4+
func emptyDatabaseQueue() -> DatabaseQueue {
5+
let dbQueue = DatabaseQueue()
6+
try! migrator().migrate(dbQueue)
7+
return dbQueue
8+
}
9+
10+
/// Returns an in-memory database that contains one player.
11+
///
12+
/// - parameter playerId: The ID of the inserted player.
13+
func populatedDatabaseQueue(playerId: Int64? = nil) -> DatabaseQueue {
14+
let dbQueue = emptyDatabaseQueue()
15+
try! dbQueue.write { db in
16+
// insert a random player (and ignore generated id)
17+
_ = try Player.makeRandom(id: playerId).inserted(db)
18+
}
19+
return dbQueue
20+
}
21+
22+
/// The migrator that defines the schema of the database.
23+
private func migrator() -> DatabaseMigrator {
24+
var migrator = DatabaseMigrator()
25+
migrator.registerMigration("createPlayer") { db in
26+
try db.create(table: "player") { t in
27+
t.autoIncrementedPrimaryKey("id")
28+
t.column("name", .text).notNull()
29+
t.column("score", .integer).notNull()
30+
t.column("photoID", .integer).notNull()
31+
}
32+
}
33+
return migrator
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import GRDB
2+
import GRDBQuery
3+
import SwiftUI
4+
5+
@main
6+
struct QueryDemoApp: App {
7+
var body: some Scene {
8+
WindowGroup {
9+
AppView()
10+
}
11+
}
12+
}
13+
14+
// MARK: - Give SwiftUI access to the database
15+
//
16+
// Define a new environment key that grants access to a DatabaseQueue.
17+
//
18+
// The technique is documented at
19+
// <https://developer.apple.com/documentation/swiftui/environmentkey>.
20+
21+
private struct DatabaseQueueKey: EnvironmentKey {
22+
/// The default dbQueue is an empty in-memory database of players
23+
static let defaultValue = emptyDatabaseQueue()
24+
}
25+
26+
extension EnvironmentValues {
27+
var dbQueue: DatabaseQueue {
28+
get { self[DatabaseQueueKey.self] }
29+
set { self[DatabaseQueueKey.self] = newValue }
30+
}
31+
}
32+
33+
// In this demo app, views observe the database with the @Query property
34+
// wrapper, defined in the local Query package. Its documentation recommends to
35+
// define a dedicated initializer for `dbQueue` access, so we comply:
36+
37+
extension Query where Request.DatabaseContext == DatabaseQueue {
38+
init(_ request: Request) {
39+
self.init(request, in: \.dbQueue)
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import GRDB
2+
import GRDBQuery
3+
4+
/// A @Query request that observes the number of players in the database. This
5+
/// request is used in the preview for the database buttons
6+
/// (see DatabaseButtons.swift).
7+
struct PlayerCountRequest: Queryable {
8+
static var defaultValue: Int { 0 }
9+
10+
func publisher(in dbQueue: DatabaseQueue) -> DatabasePublishers.Value<Int> {
11+
ValueObservation
12+
.tracking(Player.fetchCount)
13+
.publisher(in: dbQueue, scheduling: .immediate)
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import Combine
2+
import GRDB
3+
import GRDBQuery
4+
5+
/// A @Query request that observes the _presence_ of the player in the database.
6+
struct PlayerPresenceRequest: Queryable {
7+
static var defaultValue: PlayerPresence { .missing }
8+
9+
var id: Int64
10+
11+
func publisher(in dbQueue: DatabaseQueue) -> AnyPublisher<PlayerPresence, Error> {
12+
ValueObservation
13+
.tracking(Player.filter(id: id).fetchOne)
14+
.publisher(in: dbQueue, scheduling: .immediate)
15+
.scan(.missing) { (previous, player) in
16+
if let player = player {
17+
return .existing(player)
18+
} else if let player = previous.player {
19+
return .gone(player)
20+
} else {
21+
return .missing
22+
}
23+
}
24+
.eraseToAnyPublisher()
25+
}
26+
}
27+
28+
enum PlayerPresence {
29+
/// The player exists in the database
30+
case existing(Player)
31+
32+
/// Player no longer exists, but we have its latest value.
33+
case gone(Player)
34+
35+
/// Player does not exist, and we don't have any information about it.
36+
case missing
37+
38+
var player: Player? {
39+
switch self {
40+
case let .existing(player), let .gone(player):
41+
return player
42+
case .missing:
43+
return nil
44+
}
45+
}
46+
47+
var exists: Bool {
48+
switch self {
49+
case .existing:
50+
return true
51+
case .gone, .missing:
52+
return false
53+
}
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import GRDB
2+
import GRDBQuery
3+
4+
/// A @Query request that observes the player (any player, actually) in the database
5+
struct PlayerRequest: Queryable {
6+
static var defaultValue: Player? { nil }
7+
8+
func publisher(in dbQueue: DatabaseQueue) -> DatabasePublishers.Value<Player?> {
9+
ValueObservation
10+
.tracking(Player.fetchOne)
11+
.publisher(in: dbQueue, scheduling: .immediate)
12+
}
13+
}

0 commit comments

Comments
 (0)