
"J'ai arraché les données de base, c'est ainsi que cela devrait fonctionner"
- Josh Holtz
"La boutique est ridiculement facile à mettre en œuvre et fait de la persistance un jeu d'enfant. C'est devenu mon premier ajout à chaque projet que je commence.
- Tyler Hillsman
"La boutique est devenue inestimable, je l'utilise dans tous les projets de côté maintenant. Ne pas avoir à se soucier de la persistance est excellent et le coût de démarrage est pratiquement nul."
- Romain Pouclet
Si vous trouvez une boutique précieuse, je l'apprécierais vraiment si vous envisagez d'aider à parrainer mon travail open source, afin que je puisse continuer à travailler sur des projets comme Boutique pour aider les développeurs comme vous.
La boutique est une bibliothèque de persistance simple mais puissante, un petit ensemble d'emballages et de types de propriété qui permettent de créer des applications étatiques incroyablement simples pour Swiftui, Uikit et AppKit. Avec sa boutique d'architecture de mise en cache à double couche + disque, fournit un moyen de créer des applications qui mettent à jour en temps réel avec un stockage complet hors ligne dans seulement quelques lignes de code en utilisant une API incroyablement simple. La boutique est construite au sommet de Bodega, et vous pouvez trouver une application de démonstration construite au sommet de l'architecture du magasin de contrôleur de vue du modèle dans ce référentiel qui vous montre comment créer une application Swiftui prête à la ligne hors ligne dans seulement quelques lignes de code. Vous pouvez en savoir plus sur la réflexion derrière l'architecture de cet article de blog explorant l'architecture MVCS.
La boutique n'a qu'un seul concept que vous devez comprendre. Lorsque vous enregistrez des données au Store , vos données seront persistées automatiquement pour vous et exposées en tant que tableau rapide régulier. Les emballages de propriété @StoredValue et @SecurelyStoredValue fonctionnent de la même manière, mais au lieu d'un tableau, ils travaillent avec des valeurs Swift singulières. Vous n'aurez jamais à penser aux bases de données, tout dans votre application est un tableau ou une valeur rapide régulière en utilisant les modèles de votre application, avec un code simple qui ressemble à toute autre application.
Vous connaissez peut-être le Store de Redux ou de l'architecture composable, mais contrairement à ces frameworks, vous n'aurez pas à vous soucier d'ajouter des actions ou des réducteurs. Avec la mise en œuvre de cette Store toutes vos données sont persistées pour vous automatiquement, aucun code supplémentaire requis. Cela vous permet de créer des applications de mise à jour en temps réel avec un support complet hors ligne de manière incroyablement simple et simple.
Vous pouvez lire un aperçu de haut niveau de la boutique ci-dessous, mais la boutique est également entièrement documentée ici.
Nous allons passer par un aperçu de haut niveau du Store ci-dessous, mais le Store est entièrement documenté avec le contexte, les cas d'utilisation et les exemples ici.
L'ensemble de la surface de l'API pour obtenir une prise en charge hors ligne complète et des mises à jour du modèle en temps réel sur l'ensemble de votre application sont trois méthodes, .insert() , .remove() et .removeAll() .
// Create a Store ¹
let store = Store < Animal > (
storage : SQLiteStorageEngine . default ( appendingPath : " Animals " ) ,
cacheIdentifier : . id
)
// Insert an item into the Store ²
let redPanda = Animal ( id : " red_panda " )
try await store . insert ( redPanda )
// Remove an animal from the Store
try await store . remove ( redPanda )
// Insert two more animals to the Store
let dog = Animal ( id : " dog " )
let cat = Animal ( id : " cat " )
try await store . insert ( [ dog , cat ] )
// You can read items directly
print ( store . items ) // Prints [dog, cat]
// You also don't have to worry about maintaining uniqueness, the Store handles uniqueness for you
let secondDog = Animal ( id : " dog " )
try await store . insert ( secondDog )
print ( store . items ) // Prints [dog, cat]
// Clear your store by removing all the items at once.
store . removeAll ( )
print ( store . items ) // Prints []
// You can even chain commands together
try await store
. insert ( dog )
. insert ( cat )
. run ( )
print ( store . items ) // Prints [dog, cat]
// This is a good way to clear stale cached data
try await store
. removeAll ( )
. insert ( redPanda )
. run ( )
print ( store . items ) // Prints [redPanda]Et si vous construisez une application Swiftui, vous n'avez rien à changer, boutique a été conçue et avec Swiftui à l'esprit. (Mais fonctionne bien dans Uikit et Appkit bien sûr.)
// Since items is a @Published property
// you can subscribe to any changes in realtime.
store . $items . sink ( { items in
print ( " Items was updated " , items )
} )
// Works great with SwiftUI out the box for more complex pipelines.
. onReceive ( store . $items , perform : {
self . allItems = $0 . filter ( { $0 . id > 100 } )
} )¹ Vous pouvez avoir autant ou aussi peu de magasins que vous le souhaitez. C'est peut-être une bonne stratégie d'avoir une boutique pour toutes les images que vous téléchargez dans votre application, mais vous voudrez peut-être également avoir une boutique par type de modèle que vous souhaitez mettre en cache. Vous pouvez même créer des magasins séparés pour les tests, la boutique n'est pas normative et le choix de la façon dont vous souhaitez modéliser vos données vous appartient. Vous remarquerez également que c'est un concept de Bodega que vous pouvez lire dans la documentation de stockage de Bodega.
² Sous le capot, le magasin fait le travail de sauvegarde de toutes les modifications du disque lorsque vous ajoutez ou supprimez les articles.
«Dans Swiftui, vous pouvez même alimenter votre View avec $items et utiliser .onReceive() pour mettre à jour et manipuler les données publiées par les $items du magasin.
AVERTISSEMENT Le stockage d'images ou d'autres données binaires dans la boutique est techniquement pris en charge mais non recommandée. La raison en est que le stockage d'images dans Boutique peut enterrer le magasin en mémoire et la mémoire de votre application en conséquence. Pour des raisons similaires car il n'est pas recommandé de stocker des images ou des blobs binaires dans une base de données, il n'est pas recommandé de stocker des images ou des taches binaires dans la boutique.
Nous allons passer par un aperçu de haut niveau de l'emballage de la propriété @Stored ci-dessous, mais @Stored est entièrement documenté avec le contexte, les cas d'utilisation et les exemples ici.
C'était facile, mais je veux vous montrer quelque chose qui rend la boutique en carrément magique. Le Store est un moyen simple de bénéficier des avantages du stockage hors ligne et des mises à jour en temps réel, mais en utilisant le wrapper de la propriété @Stored , nous pouvons mettre en cache n'importe quelle propriété en mémoire et sur disque avec une seule ligne de code.
extension Store where Item == RemoteImage {
// Initialize a Store to save our images into
static let imagesStore = Store < RemoteImage > (
storage : SQLiteStorageEngine . default ( appendingPath : " Images " )
)
}
final class ImagesController : ObservableObject {
/// Creates a @Stored property to handle an in-memory and on-disk cache of images. ⁴
@ Stored ( in : . imagesStore ) var images
/// Fetches `RemoteImage` from the API, providing the user with a red panda if the request succeeds.
func fetchImage ( ) async throws -> RemoteImage {
// Hit the API that provides you a random image's metadata
let imageURL = URL ( string : " https://image.redpanda.club/random/json " ) !
let randomImageRequest = URLRequest ( url : imageURL )
let ( imageResponse , _ ) = try await URLSession . shared . data ( for : randomImageRequest )
return RemoteImage ( createdAt : . now , url : imageResponse . url , width : imageResponse . width , height : imageResponse . height , imageData : imageResponse . imageData )
}
/// Saves an image to the `Store` in memory and on disk.
func saveImage ( image : RemoteImage ) async throws {
try await self . $images . insert ( image )
}
/// Removes one image from the `Store` in memory and on disk.
func removeImage ( image : RemoteImage ) async throws {
try await self . $images . remove ( image )
}
/// Removes all of the images from the `Store` in memory and on disk.
func clearAllImages ( ) async throws {
try await self . $images . removeAll ( )
}
} C'est ça, c'est vraiment ça. Cette technique évolue très bien, et partager ces données sur de nombreuses vues est exactement la façon dont la boutique passe des applications simples à complexes sans ajouter de complexité de l'API. Il est difficile de croire que maintenant votre application peut mettre à jour son état en temps réel avec un stockage hors ligne complet grâce à une seule ligne de code. @Stored(in: .imagesStore) var images
⁴ (Si vous préférez découpler le magasin à partir de votre modèle de vue, contrôleur ou objet Manager, vous pouvez injecter des magasins dans l'objet comme celui-ci.)
final class ImagesController : ObservableObject {
@ Stored var images : [ RemoteImage ]
init ( store : Store < RemoteImage > ) {
self . _images = Stored ( in : store )
}
} Nous allons passer par un aperçu de haut niveau de @StoredValue et @SecurelyStoredValue Propriété ci-dessous, mais ils sont entièrement documentés avec le contexte, les cas d'utilisation et les exemples ici.
Le Store et @Stored ont été créés pour stocker une gamme de données car la plupart des applications de données rendent sous la forme d'un tableau. Mais parfois, nous devons stocker une valeur individuelle, c'est là que @StoredValue et @SecurelyStoredValue sont utiles.
Que vous ayez besoin d'enregistrer une information importante pour la prochaine fois que votre application sera lancée, stocké un jeton AUTH dans le trousseau, ou que vous souhaitez modifier l'apparence d'une application en fonction des paramètres d'un utilisateur, ces configurations d'application sont des valeurs individuelles que vous voudrez persister.
Souvent, les gens choisissent de stocker des articles individuels comme celui-ci dans UserDefaults . Si vous avez utilisé @AppStorage , @StoredValue se sentira chez lui, il a une API très similaire avec quelques fonctionnalités supplémentaires. Un @StoredValue finira par être stocké dans UserDefaults , mais il expose également un publisher afin que vous puissiez facilement vous abonner aux modifications.
// Setup a `@StoredValue has the same API.
@ StoredValue ( key : " hasHapticsEnabled " )
var hasHapticsEnabled = false
// You can also store nil values
@ StoredValue ( key : " lastOpenedDate " )
var lastOpenedDate : Date ? = nil
// Enums work as well, as long as it conforms to `Codable` and `Equatable`.
@ StoredValue ( key : " currentTheme " )
var currentlySelectedTheme = . light
// Complex objects work as well
struct UserPreferences : Codable , Equatable {
var hasHapticsEnabled : Bool
var prefersDarkMode : Bool
var prefersWideScreen : Bool
var spatialAudioEnabled : Bool
}
@ StoredValue ( key : " userPreferences " )
var preferences = UserPreferences ( )
// Set the lastOpenedDate to now
$lastOpenedDate . set ( . now )
// currentlySelected is now .dark
$currentlySelectedTheme . set ( . dark )
// StoredValues that are backed by a boolean also have a toggle() function
$hasHapticsEnabled . toggle ( ) L'écran de propriété @SecurelyStoredValue peut faire tout ce que fait un @StoredValue , mais au lieu de stocker des valeurs dans UserDefaults , un @SecurelyStoredValue persistera les éléments dans le porte-clés du système. Ceci est parfait pour stocker des valeurs sensibles telles que les mots de passe ou les jetons AUTH, que vous ne voudriez pas stocker dans UserDefaults .
Si vous avez des questions, je vous demanderais de regarder la documentation en premier, la boutique et la bodega sont très fortement documentées. En plus de cette boutique, il est livré avec non pas une mais deux applications de démonstration, chacune servant un objectif différent, mais démontrant comment vous pouvez créer une application soutenue de boutique.
Pendant que je construisais V1, j'ai remarqué que les gens qui avaient de la boutique l'adoraient, et les gens qui pensaient que cela pourrait être bon mais que les questions ont grandi pour l'aimer une fois qu'ils ont compris comment l'utiliser. À cause de cela, j'ai cherché à écrire beaucoup de documentation expliquant les concepts et les cas d'utilisation courants que vous rencontrerez lors de la création d'une application iOS ou MacOS. Si vous avez encore des questions ou des suggestions, je suis très ouvert aux commentaires, comment contribuer est discuté dans la section de rétroaction bien nommée de cette lecture.
La boutique est très utile en elle-même pour créer des applications prêtes à la ligne en temps réel avec seulement quelques lignes de code, mais elle est encore plus puissante lorsque vous utilisez l'architecture de magasin de contrôleur de vue du modèle que j'ai développé, démontré dans le ImagesController ci-dessus. MVCS rassemble la familiarité et la simplicité de l'architecture MVC que vous connaissez et aimez avec la puissance d'un Store , pour donner à votre application une gestion d'état et une architecture de données simples mais bien définis.
Si vous souhaitez en savoir plus sur la façon dont cela fonctionne, vous pouvez lire sur la philosophie dans un article de blog où j'explore les MVC pour Swiftui, et vous pouvez trouver une implémentation de référence d'une application MVCS en temps réel en temps réel propulsé par Boutique dans ce repo.
Nous avons seulement gratté la surface de ce que la boutique peut faire ici. Tirant parti StorageEngine de Bodega, vous pouvez créer des pipelines de données complexes qui font tout, de la mise en cache de données à l'interfaçage avec votre serveur API. La boutique et la bodega sont plus que des bibliothèques, ils sont un ensemble de primitives pour toute application basée sur les données, donc je suggère de leur donner une photo, de jouer avec l'application de démonstration et même de créer votre propre application!
Ce projet fournit plusieurs formes de livraison des commentaires aux responsables.
Si vous avez une question sur la boutique, nous vous demandons de consulter d'abord la documentation pour voir si votre question y a été répondue.
Ce projet est fortement documenté mais comprend également plusieurs échantillons de projets.
StorageEngine personnalisé, ce projet vous servira bien comme moyen de tester les performances des opérations que vous devez créer.Si vous avez toujours une question, une amélioration ou un moyen d'améliorer la boutique, ce projet tire parti de la fonction de discussions de Github.
Si vous trouvez un bogue et souhaitez signaler un problème serait apprécié.
Le Swift Package Manager est un outil pour automatiser la distribution du code SWIFT et est intégré dans le système Swift Build.
Une fois que vous avez configuré votre package Swift, l'ajout de boutique en tant que dépendance est aussi simple que de l'ajouter à la valeur des dépendances de votre Package.swift .
dependencies: [
. package ( url : " https://github.com/mergesort/Boutique.git " , . upToNextMajor ( from : " 1.0.0 " ) )
] Si vous préférez ne pas utiliser SPM, vous pouvez intégrer manuellement la boutique dans votre projet en copiant les fichiers.
Salut, je suis Joe partout sur le Web, mais surtout sur Mastodon.
Voir la licence pour plus d'informations sur la façon dont vous pouvez utiliser la boutique.
La boutique est un travail d'amour pour aider les développeurs à créer de meilleures applications, ce qui vous permet de déverrouiller votre créativité et de faire quelque chose d'incroyable pour vous et vos utilisateurs. Si vous trouvez une boutique précieuse, je l'apprécierais vraiment si vous envisagez d'aider à parrainer mon travail open source, afin que je puisse continuer à travailler sur des projets comme Boutique pour aider les développeurs comme vous.
Maintenant que vous savez ce qui vous attend , il est temps de commencer ?