Swift裡我用這個姿勢寫UserDefaults

沒故事的卓同學發表於2017-02-22

人在江湖飄,總免不了要存一些值到UserDefaults。

UserDefaults.standard.set("@沒故事的卓同學", forKey: "Author")

let author = UserDefaults.standard.value(forKey: "Author")複製程式碼

有存就有取,還可能有很多地方會取這個值。這樣的話每次寫這個 key 就有點蛋疼了。

Swift裡我用這個姿勢寫UserDefaults

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以後的通知中心形式相似。

Swift裡我用這個姿勢寫UserDefaults

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 。

歡迎關注我的微博:@沒故事的卓同學


參考連結:
Swift: UserDefaults Protocol
UserDefault 資料儲存和讀取簡易封裝

相關文章