人在江湖飄,總免不了要存一些值到UserDefaults。
UserDefaults.standard.set("@沒故事的卓同學", forKey: "Author")
let author = UserDefaults.standard.value(forKey: "Author")複製程式碼
有存就有取,還可能有很多地方會取這個值。這樣的話每次寫這個 key 就有點蛋疼了。
key 寫成一個全域性的常量雖然解決了程式碼重複的問題,但是體驗上還是沒有改變。一個 app 裡也有不少的字串常量,怎麼表明這個字串是用於持久化的 key 呢?
解決方案
利用 rawValue 型別為 String 的列舉作為 key,通過協議為指定列舉增加存取到 UserDefaults 的能力。
首先宣告一個列舉,建議在UserDefaults的擴充套件裡寫,當然如果你想要省掉前面一個名稱空間也是可以的:
extension UserDefaults {
enum TestData: String,UserDefaultSettable {
case name
}
}複製程式碼
注意到這個列舉需要實現UserDefaultSettable
協議。
接著就可以在這個列舉裡呼叫store(value: )
方法來儲存:
UserDefaults.TestData.name.store(value: "name")
let storeValue = UserDefaults.TestData.name.storedString複製程式碼
這個列舉TestData.name
可以理解為一張銀行卡。拿著這張卡到銀行,說我要存,就夠了。這個列舉就是一個ID。
這個思路和Swift 3以後的通知中心形式相似。
Notification.Name 也是一個 rawValue 為字串的列舉。
extension NSNotification {
public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
public init(_ rawValue: String)
public init(rawValue: String)
}
}複製程式碼
當然嚴格的說並不是一個列舉,只是和列舉一樣實現了RawRepresentable
協議。不過我覺得直接宣告一個列舉在這裡會比實現RawRepresentable
便捷一些。
實現
主要就是UserDefaultSettable
協議的擴充套件了。
public protocol UserDefaultSettable {
var uniqueKey: String { get }
}
public extension UserDefaultSettable where Self: RawRepresentable, Self.RawValue == String {
public func store(value: Any?){
UserDefaults.standard.set(value, forKey: uniqueKey)
}
public var storedValue: Any? {
return UserDefaults.standard.value(forKey: uniqueKey)
}
// 為所有的key加上列舉名作為名稱空間,避免重複
public var uniqueKey: String {
return "\(Self.self).\(rawValue)"
}
public func store(value: Bool) {
// ......
}
public var storedBool: Bool {
// ......
}
// 還有支援其他儲存型別的函式,就不全寫了
}複製程式碼
主要的實現程式碼很簡單,就是為 RawValue 為 String 的列舉新增了 store 和 獲取儲存 value 的方法。
為了避免直接取列舉的名字作為key可能引起的重名,宣告瞭一個計算屬性來生成儲存的 key,規則就是加上列舉 Type 名作為字首。比如上面的例子裡儲存的 key 就是 TestData.name 。
歡迎關注我的微博:@沒故事的卓同學