Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Value an associated type of Queryable #49

Merged
merged 1 commit into from
Dec 1, 2023
Merged

Conversation

groue
Copy link
Owner

@groue groue commented Dec 1, 2023

This PR makes it possible to derive sub-protocols of Queryable.


For example, given an app that has a PlayerStore object with the following api:

import GRDB

struct PlayerStore {
    var reader: GRDB.DatabaseReader { get }
}

The app can define convenience protocols as below:

/// A `Queryable` type that performs a single fetch. It is suitable for
/// views that must not change once they have appeared on screen.
protocol FetchQueryable: Queryable
where DatabaseContext == PlayerStore,
      ValuePublisher == AnyPublisher<Value, any Error>
{
    /// Returns the fetched value.
    func fetch(_ db: Database) throws -> Value
}

extension FetchQueryable {
    func publisher(in store: PlayerStore) -> ValuePublisher {
        Deferred {
            Result {
                try store.reader.read(fetch)
            }
            .publisher
        }
        .eraseToAnyPublisher()
    }
}

/// A `Queryable` type that observes the database.
protocol ObservationQueryable: Queryable
where DatabaseContext == PlayerStore,
      ValuePublisher == AnyPublisher<Value, any Error>
{
    /// Returns the observed value.
    func fetch(_ db: Database) throws -> Value
}

extension ObservationQueryable {
    func publisher(in store: PlayerStore) -> ValuePublisher {
        ValueObservation
            .tracking(fetch)
            .publisher(in: store.reader, scheduling: .immediate)
            .eraseToAnyPublisher()
    }
}

This can dramatically simplify the definition of some Queryable types:

-struct PlayersRequest: Queryable {
+struct PlayersRequest: FetchQueryable {
     static var defaultValue: [Player] = []
     
-    func publisher(in store: PlayerStore) -> AnyPublisher<[Player], any Error> {
-        Deferred {
-            Result {
-                try store.reader.read { db in
-                    try Player.fetchAll(db)
-                }
-            }
-            .publisher
-        }
-        .eraseToAnyPublisher()
-    }
+    func fetch(_ db: Database) throws -> [Player] {
+        try Player.fetchAll(db)
+    }
 }

-struct TeamsRequest: Queryable {
+struct TeamsRequest: ObservationQueryable {
     static var defaultValue: [Team] = []
     
-    func publisher(in store: PlayerStore) -> AnyPublisher<[Team], any Error> {
-        ValueObservation
-            .tracking { db in
-                try Team.fetchAll(db)
-            }
-            .publisher(in: store.reader, scheduling: .immediate)
-            .eraseToAnyPublisher()
-    }
+    func fetch(_ db: Database) throws -> [Team] {
+        try Team.fetchAll(db)
+    }
 }

This makes it possible to derive sub-protocols of Queryable
@groue groue added the enhancement New feature or request label Dec 1, 2023
@groue groue merged commit 7d7c3b9 into main Dec 1, 2023
@groue groue deleted the dev/conveniences branch December 1, 2023 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant