
"Eu rasguei dados principais, é assim que deve funcionar"
- Josh Holtz
"A boutique é ridiculamente fácil de implementar e facilita a persistência. Tornou -se minha primeira adição a todos os projetos que inicio.
- Tyler Hillsman
"A boutique se tornou inestimável, eu a uso em todos os lados agora. Não ter que se preocupar com a persistência é grande e o custo de começar é praticamente zero".
- Romain Pouclet
Se você achar valioso boutique, eu realmente apreciaria se você considerar ajudar a patrocinar meu trabalho de código aberto, para que eu possa continuar trabalhando em projetos como a Boutique para ajudar os desenvolvedores como você.
A Boutique é uma biblioteca de persistência simples, mas poderosa, um pequeno conjunto de invólucros e tipos de propriedades que permitem a construção de aplicativos incrivelmente simples orientados pelo estado para Swiftui, Uikit e Appkit. Com sua boutique de arquitetura de cache de cache de cache de memória de camada dupla +, fornece uma maneira de criar aplicativos que atualizam em tempo real com armazenamento offline completo em apenas algumas linhas de código usando uma API incrivelmente simples. A boutique é construída no topo da Bodega e você pode encontrar um aplicativo de demonstração construído no topo da arquitetura da loja do Model View Controller neste repositório, que mostra como fazer um aplicativo Swiftui pronto para fazer apenas algumas linhas de código. Você pode ler mais sobre o pensamento por trás da arquitetura nesta postagem do blog, explorando a arquitetura MVCS.
A boutique tem apenas um conceito que você precisa entender. Ao salvar os dados na Store , seus dados serão persistidos automaticamente para você e expostos como uma matriz SWIFT regular. Os inauguradores @StoredValue e @SecurelyStoredValue funcionam da mesma maneira, mas, em vez de uma matriz, eles trabalham com valores singulares singulares. Você nunca precisará pensar em bancos de dados, tudo no seu aplicativo é uma matriz swift ou valor regular usando os modelos do seu aplicativo, com código direto que se parece com qualquer outro aplicativo.
Você pode estar familiarizado com a Store do Redux ou da arquitetura composta, mas, diferentemente das estruturas, você não precisará se preocupar em adicionar ações ou redutores. Com a implementação desta Store , todos os seus dados são persistidos para você automaticamente, nenhum código adicional é necessário. Isso permite criar aplicativos de atualização em tempo real com suporte offline completo de uma maneira incrivelmente simples e direta.
Você pode ler uma visão geral de alto nível da boutique abaixo, mas a boutique também está totalmente documentada aqui.
Passaremos por uma visão geral de alto nível da Store abaixo, mas a Store está totalmente documentada com contexto, casos de uso e exemplos aqui.
Toda a área de superfície da API para alcançar o suporte offline completo e as atualizações de modelos em tempo real em todo o seu aplicativo são três métodos, .insert() , .remove() e .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]E se você estiver construindo um aplicativo Swiftui, não precisa mudar nada, foi feito boutique para e com Swiftui em mente. (Mas funciona bem em Uikit e Appkit, é claro.)
// 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 } )
} )Você pode ter quantas ou poucas lojas quiser. Pode ser uma boa estratégia ter uma loja para todas as imagens que você baixar no seu aplicativo, mas você também pode querer ter uma loja por tipo de modelo que você gostaria de armazenar em cache. Você pode até criar lojas separadas para testes, a boutique não é prescritiva e a escolha de como você deseja modelar seus dados é seus. Você também notará que é um conceito da Bodega sobre o qual você pode ler na documentação do Bodega StorageEngine.
² sob o capô, a loja está fazendo o trabalho de economizar todas as alterações no disco quando você adiciona ou remove itens.
³ Em Swiftui, você pode até alimentar sua View com $items e usar .onReceive() para atualizar e manipular dados publicados pelos $items da loja.
Aviso O armazenamento de imagens ou outros dados binários na boutique é tecnicamente suportado, mas não recomendado. O motivo é que o armazenamento de imagens no Boutique's pode aumentar a loja na memória e a memória do seu aplicativo como resultado. Por razões semelhantes que não é recomendável armazenar imagens ou bolhas binárias em um banco de dados, não é recomendável armazenar imagens ou bolhas binárias na boutique.
Passaremos por uma visão geral de alto nível do invólucro de propriedade @Stored abaixo, mas @Stored está totalmente documentado com contexto, casos de uso e exemplos aqui.
Isso foi fácil, mas quero mostrar a você algo que faz a boutique se sentir absolutamente mágica. A Store é uma maneira simples de obter os benefícios das atualizações de armazenamento offline e em tempo real, mas usando o wrapper @Stored Property Wrapper, podemos cache qualquer propriedade na memória e no disco com apenas uma linha 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 ( )
}
} É isso, é realmente isso. Essa técnica escala muito bem, e compartilhar esses dados em muitas visualizações é exatamente como as escalas boutiques de aplicativos simples a complexos sem adicionar complexidade da API. É difícil acreditar que agora seu aplicativo possa atualizar seu estado em tempo real com armazenamento offline completo, graças a apenas uma linha de código. @Stored(in: .imagesStore) var images
⁴ (Se você preferir dissociar a loja do seu modelo de visualização, controlador ou objeto de gerente, pode injetar lojas no objeto como este.)
final class ImagesController : ObservableObject {
@ Stored var images : [ RemoteImage ]
init ( store : Store < RemoteImage > ) {
self . _images = Stored ( in : store )
}
} Passaremos por uma visão geral de alto nível das embalagens de propriedades @StoredValue e @SecurelyStoredValue abaixo, mas eles estão totalmente documentados com contexto, casos de uso e exemplos aqui.
A Store e @Stored foram criadas para armazenar uma variedade de dados, porque a maioria dos aplicativos de dados renderiza na forma de uma matriz. Mas, ocasionalmente, precisamos armazenar um valor individual, é aí que @StoredValue e @SecurelyStoredValue são úteis.
Se você precisa salvar uma informação importante para a próxima vez que seu aplicativo for iniciado, armazenou um token de autenticação no chaveiro ou você deseja alterar a aparência de um aplicativo com base nas configurações de um usuário, essas configurações de aplicativos são valores individuais que você deseja persistir.
Muitas vezes, as pessoas optam por armazenar itens individuais como esse no UserDefaults . Se você usou @AppStorage , @StoredValue parecerá em casa, ele possui uma API muito semelhante com alguns recursos adicionais. Um @StoredValue acabará sendo armazenado no UserDefaults , mas também expõe um publisher para que você possa assinar facilmente as alterações.
// 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 ( ) O wrapper de propriedades @SecurelyStoredValue pode fazer tudo o que um @StoredValue faz, mas, em vez de armazenar valores no UserDefaults , um @SecurelyStoredValue persistirá itens no chaveiro do sistema. Isso é perfeito para armazenar valores confidenciais, como senhas ou tokens de autenticação, que você não gostaria de armazenar no UserDefaults .
Se você tiver alguma dúvida, eu pediria que olhe para a documentação primeiro, tanto a boutique quanto a bodega estão muito documentadas. Além dessa boutique, vem com não um, mas dois aplicativos de demonstração, cada um atendendo a um propósito diferente, mas demonstrando como você pode criar um aplicativo apoiado por boutique.
Enquanto estava construindo V1, notei que as pessoas que conseguiram boutique adoraram, e as pessoas que pensavam que poderia ser bom, mas tiveram perguntas, cresceram para amá -lo assim que entendiam como usá -lo. Por isso, procurei escrever muita documentação explicando os conceitos e os casos de uso comum que você encontrará ao criar um aplicativo iOS ou macOS. Se você ainda tiver dúvidas ou sugestões, estou muito aberto a feedback, como contribuir é discutido na seção de feedback apropriadamente nomeado deste ReadMe.
A boutique é muito útil para criar aplicativos prontos para o offline em tempo real com apenas algumas linhas de código, mas é ainda mais poderoso quando você usa a arquitetura de lojas do Model View Controller que desenvolvi, demonstrada no ImagesController acima. Os MVCs reúnem a familiaridade e a simplicidade da arquitetura do MVC que você conhece e ama com o poder de uma Store , para dar ao seu aplicativo uma gestão de estado simples, mas bem definida.
Se você quiser saber mais sobre como funciona, pode ler sobre a filosofia em uma postagem no blog, onde exploro os MVCs para Swiftui, e você pode encontrar uma implementação de referência de um aplicativo MVCS em tempo real pronto para offline, alimentado pela Boutique neste repositório.
Só arranhamos a superfície do que a boutique pode fazer aqui. Aproveitando StorageEngine do Bodega, você pode criar pipelines de dados complexos que fazem de tudo, desde os dados de cache até a interface com o seu servidor de API. A boutique e a bodega são mais do que bibliotecas, são um conjunto de primitivas para qualquer aplicativo orientado a dados, por isso sugiro dar uma chance a eles, brincar com o aplicativo de demonstração e até mesmo criar um aplicativo!
Este projeto fornece várias formas de entrega de feedback aos mantenedores.
Se você tiver uma pergunta sobre boutique, pedimos que você consulte a documentação para ver se sua pergunta foi respondida lá.
Este projeto está fortemente documentado, mas também inclui vários projetos de amostra.
StorageEngine personalizado, este projeto o servirá bem como uma maneira de testar o desempenho das operações que você precisa criar.Se você ainda tiver uma pergunta, aprimoramento ou uma maneira de melhorar a boutique, este projeto aproveita o recurso de discussões do Github.
Se você encontrar um bug e desejar relatar um problema, seria apreciado.
O Swift Package Manager é uma ferramenta para automatizar a distribuição do código SWIFT e está integrado ao sistema Swift Build.
Depois de configurar seu pacote SWIFT, adicionar a boutique como uma dependência é tão fácil quanto adicioná -lo ao valor de dependências do seu Package.swift .
dependencies: [
. package ( url : " https://github.com/mergesort/Boutique.git " , . upToNextMajor ( from : " 1.0.0 " ) )
] Se você preferir não usar o SPM, poderá integrar a boutique ao seu projeto manualmente copiando os arquivos.
Olá, estou Joe em todos os lugares da web, mas especialmente no Mastodon.
Consulte a licença para obter mais informações sobre como você pode usar a Boutique.
A Boutique é um trabalho de amor para ajudar os desenvolvedores a criar aplicativos melhores, facilitando a desbloqueio de sua criatividade e tornar algo incrível para você e seus usuários. Se você achar valioso boutique, eu realmente apreciaria se você considerar ajudar a patrocinar meu trabalho de código aberto, para que eu possa continuar trabalhando em projetos como a Boutique para ajudar os desenvolvedores como você.
Agora que você sabe o que está reservado para você, é hora de começar ?