
"Arrancé datos principales, esta es la forma en que debería funcionar"
- Josh Holtz
"Boutique es ridículamente fácil de implementar y hace que la persistencia sea muy fácil. Se ha convertido en mi primera incorporación a cada proyecto que comience.
- Tyler Hillsman
"Boutique se ha vuelto invaluable, ahora lo uso en cada proyecto lateral. No tener que tener que preocuparse por la persistencia es excelente y el costo de comenzar es prácticamente cero".
- Romain Pouclet
Si encuentra una valiosa boutique, realmente agradecería que considere ayudar a patrocinar mi trabajo de código abierto, para que pueda continuar trabajando en proyectos como Boutique para ayudar a desarrolladores como usted.
Boutique es una biblioteca de persistencia simple pero poderosa, un pequeño conjunto de envoltorios y tipos de propiedades que permiten construir aplicaciones increíblemente simples impulsadas por el estado para Swiftui, Uikit y Appkit. Con su boutique de arquitectura de almacenamiento en caché de disco + memoria de doble capa, proporciona una forma de crear aplicaciones que se actualizan en tiempo real con un almacenamiento completo fuera de línea en solo unas pocas líneas de código utilizando una API increíblemente simple. Boutique está construida en la cima de Bodega, y puede encontrar una aplicación de demostración construida sobre la arquitectura de la tienda del controlador de vista de modelo en este repositorio que le muestra cómo hacer una aplicación Swiftui lista para fuera de línea en solo unas pocas líneas de código. Puede leer más sobre el pensamiento detrás de la arquitectura en esta publicación de blog que explora la arquitectura MVCS.
Boutique solo tiene un concepto que debes entender. Cuando guarde datos en el Store sus datos persistirán automáticamente para usted y se expondrá como una matriz rápida regular. Los envoltorios de propiedades @StoredValue y @SecurelyStoredValue funcionan de la misma manera, pero en lugar de una matriz funcionan con valores singulares Swift. Nunca tendrá que pensar en bases de datos, todo en su aplicación es una matriz o valor regular de Swift usando los modelos de su aplicación, con un código directo que se parece a cualquier otra aplicación.
Es posible que esté familiarizado con la Store de Redux o la arquitectura compuesta, pero a diferencia de esos marcos, no necesitará preocuparse por agregar acciones o reductores. Con esta implementación Store , todos sus datos se persisten automáticamente, no se requiere código adicional. Esto le permite crear aplicaciones de actualización en tiempo real con soporte completo fuera de línea de una manera increíblemente simple y directa.
Puede leer una descripción general de alto nivel de Boutique a continuación, pero Boutique también está completamente documentada aquí.
Pasaremos por una descripción general de alto nivel de la Store a continuación, pero la Store está completamente documentada con contexto, casos de uso y ejemplos aquí.
Toda la superficie de la API para lograr el soporte completo fuera de línea y las actualizaciones del modelo en tiempo real en toda su aplicación son tres métodos, .insert() , .remove() y .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]Y si estás construyendo una aplicación Swiftui, no tienes que cambiar nada, Boutique fue hecha y con Swiftui en mente. (Pero funciona bien en Uikit y Appkit, por supuesto).
// 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 } )
} )¹ Puede tener tantas o tan pocas tiendas como desee. Puede ser una buena estrategia tener una tienda para todas las imágenes que descargue en su aplicación, pero también es posible que desee tener una tienda por modelo de tipo que desea almacenar en caché. Incluso puede crear tiendas separadas para pruebas, Boutique no es prescriptiva y la opción de cómo le gustaría modelar sus datos es suyo. También notará, ese es un concepto de Bodega sobre el que puede leer en la documentación de almacenamiento de Bodega.
² Debajo del capó, la tienda está haciendo el trabajo de guardar todos los cambios en el disco cuando agrega o elimina los elementos.
³ En Swiftui incluso puede alimentar sus View con $items y usar .onReceive() para actualizar y manipular datos publicados por los $items de la tienda.
Advertencia almacenar imágenes u otros datos binarios en boutique es técnicamente compatible pero no se recomienda. La razón es que el almacenamiento de imágenes en Boutique puede balancear la tienda en memoria y como resultado la memoria de su aplicación. Por razones similares, ya que no se recomienda almacenar imágenes o manchas binarias en una base de datos, no se recomienda almacenar imágenes o manchas binarias en Boutique.
Pasaremos por una descripción general de alto nivel del contenedor de propiedades @Stored a continuación, pero @Stored está completamente documentado con contexto, casos de uso y ejemplos aquí.
Eso fue fácil, pero quiero mostrarte algo que haga que Boutique se sienta francamente mágica. La Store es una forma simple de obtener los beneficios del almacenamiento fuera de línea y las actualizaciones de tiempo real, pero al usar el contenedor de propiedades @Stored podemos almacenar en caché cualquier propiedad en memoria y en el disco con solo una línea de código.
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 ( )
}
} Eso es todo, eso es realmente todo. Esta técnica escala muy bien, y compartir estos datos en muchas vistas es exactamente cómo boutique escala de aplicaciones simples a complejas sin agregar complejidad de API. Es difícil creer que ahora su aplicación pueda actualizar su estado en tiempo real con un almacenamiento completo fuera de línea gracias a una sola línea de código. @Stored(in: .imagesStore) var images
⁴ (Si prefiere desacoplar la tienda desde su modelo de vista, controlador o objeto Manager, puede inyectar tiendas en el objeto como este).
final class ImagesController : ObservableObject {
@ Stored var images : [ RemoteImage ]
init ( store : Store < RemoteImage > ) {
self . _images = Stored ( in : store )
}
} Pasaremos por una descripción general de alto nivel de @StoredValue y @SecurelyStoredValue Propiedades a continuación, pero están completamente documentados con contexto, casos de uso y ejemplos aquí.
La Store y @Stored se crearon para almacenar una variedad de datos porque la mayoría de las aplicaciones de datos se presentan en forma de una matriz. Pero ocasionalmente necesitamos almacenar un valor individual, ahí es donde @StoredValue y @SecurelyStoredValue son útiles.
Ya sea que necesite guardar una información importante para la próxima vez que se inicie su aplicación, almacene un token de autenticación en el llavero, o desea cambiar cómo se ve una aplicación en función de la configuración de un usuario, esas configuraciones de aplicaciones son valores individuales que desea persistir.
Muchas veces las personas optarán por almacenar artículos individuales como ese en UserDefaults . Si ha usado @AppStorage , @StoredValue se sentirá como en casa, tiene una API muy similar con algunas características adicionales. Un @StoredValue terminará siendo almacenado en UserDefaults , pero también expone a un publisher para que pueda suscribirse fácilmente a los cambios.
// 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 ( ) El contenedor de propiedades @SecurelyStoredValue puede hacer todo lo que hace un @StoredValue , pero en lugar de almacenar valores en UserDefaults , un @SecurelyStoredValue persistirá elementos en el llavero del sistema. Esto es perfecto para almacenar valores confidenciales como contraseñas o tokens de autenticación, que no desea almacenar en UserDefaults .
Si tiene alguna pregunta, le pediría que primero mire la documentación, tanto Boutique como Bodega están muy documentados. Además de esa boutique viene con no una sino dos aplicaciones de demostración, cada una de ellas con un propósito diferente pero demostrando cómo puede construir una aplicación respaldada por boutique.
Mientras estaba construyendo V1, noté que a las personas que obtuvieron boutique le encantaban, y a las personas que pensaban que podría ser buena, pero tenía preguntas para amarlo una vez que entendieron cómo usarlo. Debido a eso, busqué escribir mucha documentación explicando los conceptos y los casos de uso comunes que encontrará al construir una aplicación iOS o MacOS. Si aún tiene preguntas o sugerencias, estoy muy abierto a los comentarios, cómo contribuir se discute en la sección de comentarios con nombre de este readme.
Boutique es muy útil por sí sola para construir aplicaciones listas para fuera de línea en tiempo real con solo unas pocas líneas de código, pero es aún más potente cuando usa la arquitectura de la tienda de controladores de vista de modelos que he desarrollado, demostrado en las ImagesController anteriores. MVCS reúne la familiaridad y la simplicidad de la arquitectura MVC que conoce y ama con el poder de una Store , para darle a su aplicación una arquitectura de datos y gestión estatal simple pero bien definida.
Si desea obtener más información sobre cómo funciona, puede leer sobre la filosofía en una publicación de blog en la que exploro MVCS para Swiftui, y puede encontrar una implementación de referencia de una aplicación MVCS realizada por Boutique en este repositorio.
Solo hemos arañado la superficie de lo que Boutique puede hacer aquí. Aprovechando StorageEngine de Bodega Puede construir tuberías de datos complejas que hagan todo, desde los datos de almacenamiento en caché hasta la interacción con su servidor API. Boutique y Bodega son más que bibliotecas, son un conjunto de primitivas para cualquier aplicación basada en datos, por lo que sugiero darles una oportunidad, jugar con la aplicación de demostración e incluso construir una aplicación propia.
Este proyecto proporciona múltiples formas de entrega de comentarios a los mantenedores.
Si tiene una pregunta sobre Boutique, le pedimos que primero consulte la documentación para ver si su pregunta ha sido respondida allí.
Este proyecto está muy documentado, pero también incluye múltiples proyectos de muestra.
StorageEngine personalizado, este proyecto le servirá bien como una forma de probar el rendimiento de las operaciones que necesita para construir.Si aún tiene una pregunta, mejora o una forma de mejorar la boutique, este proyecto aprovecha la función de discusiones de GitHub.
Si encuentra un error y desea informar un problema sería apreciado.
El Swift Package Manager es una herramienta para automatizar la distribución del código Swift y está integrado en el sistema de compilación Swift.
Una vez que tenga su paquete Swift configurado, agregar boutique como dependencia es tan fácil como agregarlo al valor de dependencias de su Package.swift .
dependencies: [
. package ( url : " https://github.com/mergesort/Boutique.git " , . upToNextMajor ( from : " 1.0.0 " ) )
] Si prefiere no usar SPM, puede integrar Boutique en su proyecto manualmente copiando los archivos.
Hola, soy Joe en todas partes en la web, pero especialmente en Mastodon.
Consulte la licencia para obtener más información sobre cómo puede usar Boutique.
Boutique es una mano de obra para ayudar a los desarrolladores a construir mejores aplicaciones, lo que le facilita el desbloqueo de su creatividad y hacer algo increíble para usted y sus usuarios. Si encuentra una valiosa boutique, realmente agradecería que considere ayudar a patrocinar mi trabajo de código abierto, para que pueda continuar trabajando en proyectos como Boutique para ayudar a desarrolladores como usted.
Ahora que sabes lo que te espera , ¿es hora de comenzar ?