iOS數(shù)據(jù)持久化UserDefaults封裝器使用詳解
使用屬性封裝器來(lái)完美創(chuàng)建UserDefaults封裝器
想象一下,你有一個(gè)應(yīng)用想實(shí)現(xiàn)自動(dòng)登錄功能。你用UserDefaults封裝了關(guān)于UserDefaults的讀與寫(xiě)邏輯。你會(huì)用UserDefaults封裝來(lái)保持對(duì)自動(dòng)登錄”On/Off“狀態(tài)、userName的跟蹤。你可能會(huì)以下面這種方式來(lái)封裝UserDefaults
struct AppData { private static let enableAutoLoginKey = "enable_auto_login_key" private static let usernameKey = "username_key" static var enableAutoLogin: Bool { get { return UserDefaults.standard.bool(forKey: enableAutoLoginKey) } set { UserDefaults.standard.set(newValue, forKey: enableAutoLoginKey) } } static var username: String { get { return UserDefaults.standard.string } set { UserDefaults.standard.set(newValueds, forKey: usernameKey) } } }
通過(guò)Swift5.1對(duì)于屬性封裝器的介紹,我們可以對(duì)上面的代碼進(jìn)行精簡(jiǎn),如下
struct AppData { @Storage(key: "enable_auto_login_key", defaultValue: false) static var enableAutoLogin: Bool @Storage(key: "username_key", defaultValue: "") static var username: String }
這樣就很完美了嗎?接著看
什么是屬性封裝器?
在我們進(jìn)入詳細(xì)討論之前,我們先快速地了解一下什么是屬性封裝器 基本上來(lái)講,屬性封裝器是一種通用數(shù)據(jù)結(jié)構(gòu),可以攔截屬性的讀寫(xiě)訪問(wèn),從而允許在屬性的讀寫(xiě)期間添加自定義行為。
可以通過(guò)關(guān)鍵字@propertyWrapper
來(lái)聲明一個(gè)屬性封裝器。你想要有一個(gè)字符串類(lèi)型的屬性,每當(dāng)這個(gè)屬性被進(jìn)行讀寫(xiě)操作的時(shí)候,控制臺(tái)就會(huì)輸出。你可以創(chuàng)建一個(gè)名為Printable
的屬性封裝器,如下:
@propertyWrapper struct Printable { private var value: String = "" var wrapperValue: String { get { print("get value:\(value)") return value } set { print("set value:\(newValue)") value = newValue } } }
通過(guò)上述代碼我們可以看出,屬性封裝跟其他struct
一樣。然而,當(dāng)定義一個(gè)屬性封裝器的時(shí)候,必須要有一個(gè)wrapppedValue
。 wrapppedValue
get
set
代碼塊就是攔截和執(zhí)行你想要的操作的地方。在這個(gè)例子中,添加了打印狀態(tài)的代碼來(lái)輸出get和set的值
接下來(lái),我們看看,如何使用Printable屬性封裝器
struct Company { @Printable static var name: String } Company.name = "Adidas" Company.name
需要注意的是,我們?nèi)绾问褂?code>@符號(hào)來(lái)聲明一個(gè)用屬性封裝器封裝的”name“變量。如果你想要在Playground中嘗試敲出上述代碼的話,你會(huì)看到以下輸出:
Set Value: Adidas
Get Value: Adidas
什么是UserDefault封裝器
在理解了什么是屬性封裝器以及它是如何工作的之后,我們現(xiàn)在開(kāi)始準(zhǔn)備實(shí)現(xiàn)我們的UserDefaults
封裝器。總結(jié)一下,我們的屬性封裝器需要持續(xù)跟蹤自動(dòng)登錄的”On/Off“狀態(tài)以及用戶的username。 通過(guò)使用我們上述討論的概念,我們可以很輕松的將Printable
屬性封裝器轉(zhuǎn)化為在讀寫(xiě)操作期間進(jìn)行讀寫(xiě)的屬性封裝器。
import Foundation @propertyWrapper struct Storage { private let key: String private let defaultValue: String init(key: Stirng, defaultValue: String) { self.key = key self.defaultValue = defaultValue } var wrappedValue: String { get { return UserDefaults.standard.string(forKey: key) ?? defaultValue } set { UserDefaults.standard.set(newValue, forKey: key) } } }
在這里,我們將我們的屬性封裝器命名為Storage
。有兩個(gè)屬性,一個(gè)是key
,一個(gè)是defaultValue
。key
將作為UserDefaults
讀寫(xiě)時(shí)的鍵,而defaultValue
則作為UserDefaults
無(wú)值時(shí)候的返回值。
Storage
屬性封裝器準(zhǔn)備就緒后,我們就可以開(kāi)始實(shí)現(xiàn)UserDefaults
封裝器了。直截了當(dāng),我們只需要?jiǎng)?chuàng)建一個(gè)被Storage
屬性封裝器封裝的‘username’變量。這里要注意的是,你可以通過(guò)key
和defaultValue
來(lái)初始化Storage
。
struct AppData { @Storage(key: "username_key", defaultValue: "") static var username: String }
一切就緒之后,UserDefaults
封裝器就可以使用了
AppData.username = "swift-senpai" print(AppData.username)
同時(shí),我們來(lái)添加enableAutoLogin
變量到我們的UserDefaults
封裝器中
struct AppData { @Storage(key: "username_key", defaultValue: "") static var username: String @Storage(key: "enable_auto_login_key", defaultValue: false) static var username: Bool }
這個(gè)時(shí)候,會(huì)報(bào)下面兩種錯(cuò)誤:
Cannot convert value of type ‘Bool’ to expected argument type ‘String’
Property type 'Bool' does not match that of lthe 'WrappedValue' property of its wrapper type 'Storage'
這是因?yàn)槲覀兊姆庋b器目前只支持String
類(lèi)型。想要解決這兩個(gè)錯(cuò)誤,我們需要將我們的屬性封裝器進(jìn)行通用化處理
將屬性封裝器進(jìn)行通用化處理
我們必須改變屬性封裝器的wrappedValue
的數(shù)據(jù)類(lèi)型來(lái)進(jìn)行封裝器的通用化處理,將String
類(lèi)型改成泛型T
。進(jìn)而,我們必須使用通用方式從UserDefaults
讀取來(lái)更新wrappedValue
get
代碼塊
@propertyWrapper struct Storage<T> { private let key: String private let defaultValue: T init(key: String, defaultValue: T) { self.key = key self.defaultValue = defaultValue } var wrappedValue: T { get { // Read value from UserDefaults return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue } set { // Set value to UserDefaults UserDefaults.standard.set(newValue, forKey: key) } } }
好,有了通用屬性封裝器之后,我們的UserDefaults
封裝器就可以存儲(chǔ)Bool類(lèi)型的數(shù)據(jù)了
// The UserDefaults wrapper struct AppData { @Storage(key: "username_key", defaultValue: "") static var username: String @Storage(key: "enable_auto_login_key", defaultValue: false) static var enableAutoLogin: Bool } AppData.enableAutoLogin = true print(AppData.enableAutoLogin) // true
存儲(chǔ)自定義對(duì)象
上面的操作都是用來(lái)基本數(shù)據(jù)類(lèi)型的。但是如果我們想要存儲(chǔ)自定義對(duì)象呢?接下來(lái)我們一起看看,如何能讓UserDefaults
支持自定義對(duì)象的存儲(chǔ)
這里的內(nèi)容很簡(jiǎn)單,我們將會(huì)存儲(chǔ)一個(gè)自定義對(duì)象到UserDefaults
中,為了達(dá)到這個(gè)目的,我們必須改造一下Storage
屬性封裝器的類(lèi)型T
,使其遵循Codable
協(xié)議
然后,在wrappedValue``set
代碼塊中我們將使用JSONEncoder
把自定義對(duì)象轉(zhuǎn)化為Data,并將其寫(xiě)入UserDefaults
中。同時(shí),在wrappedValue``get
代碼塊中,我們將使用JSONDecoder
把從UserDefaults
中讀取的數(shù)據(jù)轉(zhuǎn)化成對(duì)應(yīng)的數(shù)據(jù)類(lèi)型。 如下:
@propertyWrapper struct Storage<T: Codable> { private let key: String private let defaultValue: T init(key: String, defaultValue: T) { self.key = key self.defaultValue = defaultValue } var wrappedValue: T { get { // Read value from UserDefaults guard let data = UserDefaults.standard.object(forKey: key) as? Data else { // Return defaultValue when no data in UserDefaults return defaultValue } // Convert data to the desire data type let value = try? JSONDecoder().decode(T.self, from: data) return value ?? defaultValue } set { // Convert newValue to data let data = try? JSONEncoder().encode(newValue) // Set value to UserDefaults UserDefaults.standard.set(data, forKey: key) } } }
為了讓大家看到如何使用更新后的Storage
屬性封裝器,我們來(lái)看一下接下來(lái)的例子。 想象一下,你需要存儲(chǔ)用戶登錄成功后服務(wù)端返回的用戶信息。首先,需要一個(gè)持有服務(wù)端返回的用戶信息的struct。這個(gè)struct必須遵循Codable
協(xié)議,以至于他能被轉(zhuǎn)化為Data存儲(chǔ)到UserDefaults
中
struct User: Codable { var firstName: String var lastName: String var lastLogin: Date? }
接下來(lái),在UserDefaults
封裝器中聲明一個(gè)User
對(duì)象
struct AppData { @Storage(key: "username_key", defaultValue: "") static var username: String @Storage(key: "enable_auto_login_key", defaultValue: false) static var enableAutoLogin: Bool // Declare a User object @Storage(key: "user_key", defaultValue: User(firstName: "", lastName: "", lastLogin: nil)) static var user: User }
搞定了,UserDefaults
封裝器現(xiàn)在可以存儲(chǔ)自定義對(duì)象了
let johnWick = User(firstName: "John", lastName: "Wick", lastLogin: Date()) // Set custom object to UserDefaults wrapper AppData.user = johnWick print(AppData.user.firstName) // John print(AppData.user.lastName) // Wick print(AppData.user.lastLogin!) // 2019-10-06 09:40:26 +0000
以上就是iOS數(shù)據(jù)持久化UserDefaults封裝器使用詳解的詳細(xì)內(nèi)容,更多關(guān)于iOS數(shù)據(jù)持久化UserDefaults的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS程序開(kāi)發(fā)中設(shè)置UITableView的全屏分隔線的方法(不畫(huà)線)
ableView是app開(kāi)發(fā)中常用到的控件,功能很強(qiáng)大,多用于數(shù)據(jù)的顯示。下面給大家介紹設(shè)置UITableView的全屏分隔線的兩種方法2016-04-04iOS實(shí)現(xiàn)九宮格連線手勢(shì)解鎖
這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)九宮格連線手勢(shì)解鎖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04iOS 超級(jí)簽名之描述文件的實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了iOS 超級(jí)簽名實(shí)現(xiàn)之描述文件的實(shí)現(xiàn)過(guò)程,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02iOS通過(guò)UIDocumentInteractionController實(shí)現(xiàn)應(yīng)用間傳文件
這篇文章主要為大家介紹了iOS通過(guò)UIDocumentInteractionController實(shí)現(xiàn)應(yīng)用間傳文件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01iOS中創(chuàng)建Model的最佳實(shí)踐記錄
這篇文章主要給大家介紹了關(guān)于iOS中創(chuàng)建Model的最佳實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用iOS具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10iOS遍歷集合(NSArray、NSDictionary、NSSet)的方法總結(jié)
這篇文章主要介紹了iOS集合遍歷(NSArray、NSDictionary、NSSet)的方法,文中給出了詳細(xì)的方法示例,并總結(jié)了各個(gè)方法的優(yōu)缺點(diǎn)來(lái)供大家學(xué)習(xí)參考,需要的朋友們下面來(lái)一起看看吧。2017-03-03解決ios微信下vue項(xiàng)目組件切換并自動(dòng)播放音頻問(wèn)題
最近在做一個(gè)英語(yǔ)答題項(xiàng)目,項(xiàng)目需求是通過(guò)答題取的成績(jī) , 答題的題型是分為,聽(tīng)音選圖,看圖選詞和填空題.下面小編給大家?guī)?lái)了ios微信下vue項(xiàng)目組件切換并自動(dòng)播放音頻的解決方案,需要的朋友參考下吧2018-01-01IOS開(kāi)發(fā)中使用UIFont設(shè)置字體及批量創(chuàng)建控件
這篇文章主要介紹了IOS開(kāi)發(fā)中使用UIFont設(shè)置字體及批量創(chuàng)建控件的方法,內(nèi)容很實(shí)用,感興趣的小伙伴們可以參考一下2016-03-03