
“我剝奪了核心數據,這是它應該運行的方式”
- 喬什·霍爾茨(Josh Holtz)
“精品店很容易實施,並使持久性變得輕而易舉。這已成為我開始每個項目的第一個補充。
- 泰勒·希爾斯曼(Tyler Hillsman)
“精品店變得無價之寶,我現在在每個附帶項目中都使用它。不必關心持久性是很棒的,而且入門的成本實際上是零。”
- 羅曼·普克萊特(Romain Pouclet)
如果您覺得精品店有價值,如果您考慮幫助贊助我的開源工作,我將非常感謝它,這樣我就可以繼續從事精品店等項目來幫助像您這樣的開發人員。
精品店是一個簡單但功能強大的持久庫,是一小部分屬性包裝紙和類型,可為SwiftUI,Uikit和AppKit構建令人難以置信的簡單狀態驅動應用程序。憑藉其雙層內存 +磁盤緩存體系結構精品店提供了一種構建應用程序的方法,可以使用非常簡單的API在幾行代碼中使用完整的離線存儲進行實時更新。 Boutique是在Bodega頂部建造的,您可以在此存儲庫中找到一個模型視圖控制器架構上構建的演示應用程序,該應用程序僅在幾行代碼中向您展示如何製作一個脫機的SwiftUi應用程序。您可以在此博客文章中探索MVCS架構中的架構背後的思維更多信息。
精品店只有一個您需要了解的概念。當您將數據保存到Store時,您的數據將自動持續為您,並作為常規的Swift數組暴露。 @StoredValue和@SecurelyStoredValue屬性包裝紙以相同的方式工作,但是它們沒有一個數組,它們可以使用單數迅速的值。您將永遠不必考慮數據庫,應用程序中的所有內容都是常規的Swift陣列或使用應用程序模型的價值,其直接代碼看起來像任何其他應用程序。
您可能會熟悉Redux或Composable Architecture的Store ,但是與那些框架不同,您不必擔心添加操作或還原器。使用此Store實現,您的所有數據都會自動持續使用,無需其他代碼。這使您可以以一種非常簡單明了的方式構建具有完整離線支持的實時更新應用程序。
您可以在下面閱讀精品店的高級概述,但是在這裡也充分記錄了精品店。
我們將在下面的Store進行高級概述,但是在此處,該Store已充分記錄了上下文,用例和示例。
API的整個表面積用於在整個應用程序中實現完整的離線支持和實時模型更新,為三種方法, .insert() ,. .remove()和.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]而且,如果您正在構建SwiftUi應用程序,則不必更改某事,就可以牢記Swiftui的精品店。 (但是當然可以在Uikit和Appkit中運作良好。)
// 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 } )
} )Å您可以擁有想要的數量或很少的商店。為您在應用程序中下載的所有圖像擁有一家商店可能是一個很好的策略,但是您可能還希望每種型號的商店都需要加速。您甚至可以為測試創建單獨的商店,精品店不是規定性的,並且選擇了您想建模數據的選擇。您還會注意到,這是Bodega的一個概念,您可以在Bodega的StorageEngine文檔中閱讀。
²在添加或刪除物品時,商店正在做的工作將所有更改保存到磁盤上。
³在SwiftUi中,您甚至可以用$items為View供電,並使用.onReceive()來更新和操縱商店$items發布的數據。
在技術上支持警告在精品店中存儲圖像或其他二進制數據,但不建議使用。原因是將圖像存儲在精品店可以在內存商店中膨脹,因此您的應用程序的內存。出於類似的原因,不建議將圖像或二進制BLOB存儲在數據庫中,因此不建議將圖像或二進制斑點存儲在精品店中。
我們將在下面瀏覽@Stored屬性包裝器的高級概述,但是@Stored已在此處充分記錄了上下文,用例和示例。
那很容易,但是我想向您展示一些使精品般的魔術。該Store是獲得離線存儲和實時更新的好處的簡單方法,但是通過使用@Stored屬性包裝器,我們可以使用一行代碼來緩存任何內存和磁盤上的任何屬性。
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 ( )
}
}就是這樣,就是這樣。該技術的擴展非常好,並且在許多視圖中共享此數據正是精品店在不增加API複雜性的情況下從簡單到復雜的應用程序縮放。很難相信,現在您的應用程序可以通過完整的離線存儲實時更新其狀態,這要歸功於一行代碼。 @Stored(in: .imagesStore) var images
⁴(如果您希望將商店從視圖模型,控制器或管理器對象分離,則可以將商店注入這樣的對象。)
final class ImagesController : ObservableObject {
@ Stored var images : [ RemoteImage ]
init ( store : Store < RemoteImage > ) {
self . _images = Stored ( in : store )
}
}我們將在下面瀏覽@StoredValue和@SecurelyStoredValue屬性包裝器的高級概述,但在此處充分記錄了它們的上下文,用例和示例。
由於大多數數據應用程序渲染以數組的形式出現,因此創建了Store和@Stored來存儲數據數組。但是有時我們需要存儲一個個人價值,這就是@StoredValue和@SecurelyStoredValue派上用場的地方。
無論您需要在下次啟動應用程序時保存重要的信息,在鑰匙扣中存儲了一個auth令牌,還是想根據用戶的設置更改應用程序的外觀,這些應用程序配置都是您想要持續存在的單個值。
通常,人們會選擇將類似的物品存儲在UserDefaults中。如果您使用過@AppStorage ,則@StoredValue會感到賓至如歸,它具有非常相似的API,並具有一些其他功能。 @StoredValue最終將存儲在UserDefaults中,但它也會暴露出publisher ,因此您可以輕鬆地訂閱更改。
// 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 ( ) @SecurelyStoredValue屬性包裝器可以做@StoredValue所做的一切,但與其在UserDefaults中存儲值, @SecurelyStoredValue將在系統的鍵鏈中持續存在。這非常適合存儲敏感值,例如密碼或驗證令牌,您不想將其存儲在UserDefaults中。
如果您有任何疑問,我會問您先查看文檔,精品店和Bodega都非常有記錄。最重要的是,這家精品店沒有一個,而是兩個演示應用程序,每個應用程序都有不同的目的,而是說明如何構建精品店支持的應用程序。
當我構建V1時,我注意到那些喜歡精品店的人喜歡它,一旦他們理解如何使用它,人們認為這可能是好的,但越來越喜歡它。因此,我試圖編寫大量文檔,解釋構建iOS或MacOS應用時您會遇到的概念和常見用例。如果您仍然有疑問或建議,我對反饋非常願意,那麼在此讀書中恰當地命名的反饋部分討論瞭如何貢獻。
精品店對僅使用幾行代碼構建實時脫機應用程序非常有用,但是當您使用我開發的Model View Controler存儲架構時,它在上面的ImagesController中進行了演示時,它甚至更強大。 MVCS匯集了您所知道的MVC體系結構的熟悉和簡單性,並藉助Store的力量,為您的應用程序提供了簡單但定義明確的狀態管理和數據架構。
如果您想了解更多有關它的工作方式的信息,您可以在我探索SwiftUI的MVC的博客文章中閱讀有關哲學的信息,並且可以在本倉庫中找到由Boutique提供支持的脫機實時MVCS應用程序的參考實現。
我們只刮過了精品店在這裡可以做什麼的表面。利用Bodega的StorageEngine您可以構建複雜的數據管道,從緩存數據到與API服務器接口的所有功能。精品店和Bodega不僅僅是圖書館,它們是任何數據驅動應用程序的一組原始圖,因此我建議給他們射擊,玩演示應用程序,甚至構建自己的應用程序!
該項目提供了多種形式的向維護者提供反饋。
如果您對精品店有疑問,我們要求您首先諮詢文檔,以查看您的問題是否已在那裡回答。
該項目已大量記錄,但還包括多個示例項目。
StorageEngine則該項目將為您服務,以測試您需要構建的操作的性能。如果您仍然有一個問題,增強或改善精品店的方法,則該項目利用Github的討論功能。
如果您發現一個錯誤並希望報告問題,將不勝感激。
Swift軟件包管理器是自動化Swift代碼分佈的工具,並將其集成到Swift Build System中。
設置了Swift軟件包後,將精品店添加為依賴項就像將其添加到Package.swift的依賴項值中一樣容易。
dependencies: [
. package ( url : " https://github.com/mergesort/Boutique.git " , . upToNextMajor ( from : " 1.0.0 " ) )
] 如果您不想使用SPM,則可以通過複製文件將精品列入項目。
嗨,我在網上無處不在,尤其是在Mastodon上。
有關如何使用精品店的更多信息,請參見許可證。
精品店是一項熱愛的勞動,旨在幫助開發人員構建更好的應用程序,從而使您更容易釋放創造力並為自己和用戶提供驚人的東西。如果您覺得精品店有價值,如果您考慮幫助贊助我的開源工作,我將非常感謝它,這樣我就可以繼續從事精品店等項目來幫助您像您這樣的開發人員。
現在您知道了為您準備的東西,該開始開始了嗎?