Created
November 18, 2025 05:03
-
-
Save robertvunabandi/a9edd11763fe0b8be890d7a2907ee29d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // DataStore.swift | |
| // Reading Journal | |
| // | |
| // | |
| import Foundation | |
| /// The job of a ``DataStore`` is simply to store the data that's been fetched | |
| /// and organize it. The data store's data is managed using a Singleton pattern, | |
| /// so there's only ever one of these in the entire app. | |
| /// | |
| /// A few possible cases where this is especially useful: | |
| /// - If the same data is fetched twice, a data store will know to not store duplicates | |
| /// - When users log out (or when there's an inconsistency in the data), the datastore | |
| /// will know to delete invalid data. | |
| /// | |
| /// **IMPORTANT:** A decision decision has been made, from the server side, | |
| /// that is very important to the way things are expected to work here. Namely: it is | |
| /// not possible to make an edit to a model that will cascade another edit into | |
| /// another model. Here's an example of a classic violation case: Deleting an | |
| /// ``MAuthor`` would result in the deletion of all of that authors' sources. | |
| /// However, we make it impossible to delete an author that still has sources in | |
| /// one's library. We will ensure this constraint applies to all types throughout | |
| /// all server calls. What this decision enables is that we can independently | |
| /// update each data store without needing to update other data stores. | |
| protocol DataStore: ObservableObject { | |
| /// An associated type for this ``DataStore``, which helps us ensure that | |
| /// we always also have an associated ``DataFetcher`` and that all data | |
| /// stores are flushed. | |
| static var Enum: DataType { get } | |
| /// Erase all the data that is currently stored in this data store | |
| static func flush() | |
| /// The type of fetcher that's associated to this ``DataStore`` | |
| associatedtype Fetcher: DataFetcher where Fetcher.Store == Self | |
| /// Returns an instance of the data fetcher for this data store. This should be the | |
| /// only way to get a data fetcher throughout the app. Do not instantiate data | |
| /// fetchers directly. | |
| func fetcher() -> Fetcher | |
| /// For debugging purposes, we may want the datastore to be given preliminary | |
| /// starting data (i.e., "seed" data). The following method is a way to seed this | |
| /// data into the data store. It is up to each individual data store to seed itself | |
| /// with the particular type of ``DataStoreSeeding`` data that works for | |
| /// it. | |
| func seed(_ seeding: DataStoreSeeding) | |
| } | |
| extension DataStore { | |
| func fetcher() -> Fetcher { | |
| Fetcher(store: self) | |
| } | |
| func seedings(_ seedings: [DataStoreSeeding]) -> Self { | |
| for seeding in seedings { | |
| seed(seeding) | |
| } | |
| return self | |
| } | |
| } | |
| /// This is a data store that only has a single instance. | |
| protocol SingleInstanceDataStore: DataStore { | |
| /// Access the singleton for this datastore. | |
| static func shared(_ user: MUser) -> Self | |
| } | |
| /// This is a data store that can have multiple instances. | |
| protocol MultipleInstanceDataStore: DataStore { | |
| /// A defined ``Key`` that will allow to fetch the instance for that key. | |
| associatedtype Key: Codable, Hashable | |
| /// We want to be able to access the key for this data store so that the fetcher | |
| /// can use it when it's fetching data (we need to ensure that the data stored | |
| /// from the fetcher actually belongs to this data store, and this enables that) | |
| var key: Key { get } | |
| /// This is the way to access the various "singleton" instances. We want to | |
| /// make sure to create them only when they're needed and such. | |
| static func shared(_ user: MUser, key: Key) -> Self | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment