人在江湖飘,总免不了要存一些值到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
}
}
注意到这个枚举需要实现<code>UserDefaultSettable</code>协议。
接着就可以在这个枚举里调用<code>store(value: )</code>方法来存储:
UserDefaults.TestData.name.store(value: "name")
let storeValue = UserDefaults.TestData.name.storedString
这个枚举<code>TestData.name</code>可以理解为一张银行卡。拿着这张卡到银行,说我要存,就够了。这个枚举就是一个ID。
这个思路和Swift 3以后的通知中心形式相似。
Notification.Name 也是一个 rawValue 为字符串的枚举。
extension NSNotification {
public struct Name : RawRepresentable, Equatable, Hashable, Comparable {
public init(_ rawValue: String)
public init(rawValue: String)
}
}
当然严格的说并不是一个枚举,只是和枚举一样实现了<code>RawRepresentable</code>协议。不过我觉得直接声明一个枚举在这里会比实现<code>RawRepresentable</code>便捷一些。
实现
主要就是<code>UserDefaultSettable</code>协议的扩展了。
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 。
欢迎关注我的微博:@没故事的卓同学
网友评论
欢迎订阅《卓同学的iOS和技术分享》https://toutiao.io/subjects/22992