Swift 里我用這個姿勢寫 UserDefaults
人在江湖飄,總免不了要存一些值到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 。
歡迎關注我的微博:@沒故事的卓同學
參考鏈接:
來自:http://www.jianshu.com/p/332ea092188e