<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
想象一下,你有一個應用想實現自動登入功能。你用UserDefaults封裝了關於UserDefaults的讀與寫邏輯。你會用UserDefaults封裝來保持對自動登入”On/Off“狀態、userName的跟蹤。你可能會以下面這種方式來封裝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) } } }
通過Swift5.1對於屬性封裝器的介紹,我們可以對上面的程式碼進行精簡,如下
struct AppData { @Storage(key: "enable_auto_login_key", defaultValue: false) static var enableAutoLogin: Bool @Storage(key: "username_key", defaultValue: "") static var username: String }
這樣就很完美了嗎?接著看
在我們進入詳細討論之前,我們先快速地瞭解一下什麼是屬性封裝器 基本上來講,屬性封裝器是一種通用資料結構,可以攔截屬性的讀寫存取,從而允許在屬性的讀寫期間新增自定義行為。
可以通過關鍵字@propertyWrapper
來宣告一個屬性封裝器。你想要有一個字串型別的屬性,每當這個屬性被進行讀寫操作的時候,控制檯就會輸出。你可以建立一個名為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 } } }
通過上述程式碼我們可以看出,屬性封裝跟其他struct
一樣。然而,當定義一個屬性封裝器的時候,必須要有一個wrapppedValue
。 wrapppedValue
get
set
程式碼塊就是攔截和執行你想要的操作的地方。在這個例子中,新增了列印狀態的程式碼來輸出get和set的值
接下來,我們看看,如何使用Printable屬性封裝器
struct Company { @Printable static var name: String } Company.name = "Adidas" Company.name
需要注意的是,我們如何使用@
符號來宣告一個用屬性封裝器封裝的”name“變數。如果你想要在Playground中嘗試敲出上述程式碼的話,你會看到以下輸出:
Set Value: Adidas
Get Value: Adidas
在理解了什麼是屬性封裝器以及它是如何工作的之後,我們現在開始準備實現我們的UserDefaults
封裝器。總結一下,我們的屬性封裝器需要持續跟蹤自動登入的”On/Off“狀態以及使用者的username。 通過使用我們上述討論的概念,我們可以很輕鬆的將Printable
屬性封裝器轉化為在讀寫操作期間進行讀寫的屬性封裝器。
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
。有兩個屬性,一個是key
,一個是defaultValue
。key
將作為UserDefaults
讀寫時的鍵,而defaultValue
則作為UserDefaults
無值時候的返回值。
Storage
屬性封裝器準備就緒後,我們就可以開始實現UserDefaults
封裝器了。直截了當,我們只需要建立一個被Storage
屬性封裝器封裝的‘username’變數。這裡要注意的是,你可以通過key
和defaultValue
來初始化Storage
。
struct AppData { @Storage(key: "username_key", defaultValue: "") static var username: String }
一切就緒之後,UserDefaults
封裝器就可以使用了
AppData.username = "swift-senpai" print(AppData.username)
同時,我們來新增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 }
這個時候,會報下面兩種錯誤:
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'
這是因為我們的封裝器目前只支援String
型別。想要解決這兩個錯誤,我們需要將我們的屬性封裝器進行通用化處理
我們必須改變屬性封裝器的wrappedValue
的資料型別來進行封裝器的通用化處理,將String
型別改成泛型T
。進而,我們必須使用通用方式從UserDefaults
讀取來更新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
封裝器就可以儲存Bool型別的資料了
// 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
上面的操作都是用來基本資料型別的。但是如果我們想要儲存自定義物件呢?接下來我們一起看看,如何能讓UserDefaults
支援自定義物件的儲存
這裡的內容很簡單,我們將會儲存一個自定義物件到UserDefaults
中,為了達到這個目的,我們必須改造一下Storage
屬性封裝器的型別T
,使其遵循Codable
協定
然後,在wrappedValue``set
程式碼塊中我們將使用JSONEncoder
把自定義物件轉化為Data,並將其寫入UserDefaults
中。同時,在wrappedValue``get
程式碼塊中,我們將使用JSONDecoder
把從UserDefaults
中讀取的資料轉化成對應的資料型別。 如下:
@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
屬性封裝器,我們來看一下接下來的例子。 想象一下,你需要儲存使用者登入成功後伺服器端返回的使用者資訊。首先,需要一個持有伺服器端返回的使用者資訊的struct。這個struct必須遵循Codable
協定,以至於他能被轉化為Data儲存到UserDefaults
中
struct User: Codable { var firstName: String var lastName: String var lastLogin: Date? }
接下來,在UserDefaults
封裝器中宣告一個User
物件
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
封裝器現在可以儲存自定義物件了
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資料持久化UserDefaults封裝器使用詳解的詳細內容,更多關於iOS資料持久化UserDefaults的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45