はじめに
今回はUserDefaultsをジェネリクスとプロトコルを使って少し工夫します。
やりたいこと
普通は以下のように使うと思います。
UserDefaults.standard.set("サンプル", forKey: "sampleKey") // 書き込み
let text = UserDefaults.standard.string(forKey: "sampleKey") // 読み込み
今回は以下のように、forKeyを工夫してアクセスしやすくします。
let userDefault = UserDefault<UserDefautlsKeys.Animal>()
userDefault.set(true, forKey: .dog)
let bool = userDefault.bool(forKey: .dog)
一番簡単なのは、以下のようにStringを拡張する方法です。
extension String {
static var dog: String { return "sampleDogKey" }
static var cat: String { return "sampleCatKey" }
}
しかし、これだとUserDefaultsのkey以外でもこの文字列を使うことができ、あまり意味がありません。
今回はUserDefaultのforKeyを設定する時のみ、このような使い方をするにはどうすればよいのかをやっていこうと思います。
実装
struct Animal {
let dog: String
let cat: String
}
struct Drink {
let water: String
let milkTea: String
let cola: String
}
struct UserDefautlsKeys {
enum Animal: String, UserDefautlsKeyProtocol {
static let domain: Domain = .animal
var id: String { self.rawValue }
case dog
case cat
}
enum Drink: String, UserDefautlsKeyProtocol {
static let domain: Domain = .drink
var id: String { self.rawValue }
case water
case milkTea
case cola
}
}
enum Domain: String {
case animal
case drink
}
protocol UserDefautlsKeyProtocol {
static var domain: Domain { get }
var id: String { get }
var key: String { get }
}
extension UserDefautlsKeyProtocol {
var key: String { Self.domain.rawValue + "." + id }
}
final class UserDefault<R: UserDefautlsKeyProtocol> {
private let userDefaults: UserDefaults
init(userDefaults: UserDefaults = .standard) {
self.userDefaults = userDefaults
}
func set(_ value: Any?, forKey identifier: R) {
userDefaults.set(value, forKey: identifier.key)
}
func bool(forKey identifier: R) -> Bool? {
return userDefaults.bool(forKey: identifier.key)
}
}
解説
一番のポイントはUserDefaultのジェネリクスです。これでキーの型をUserDefautlsKeyProtocolが準拠したものに限定できます。今回であれば、UserDefautlsKeysのAnimalとDrinkに準拠させているので、これらのkey(今回はkeyを初期設定しているので、enumの中には書かれていないが、domainとidで一意に決まる)に限定できます。
また、プロトコルを使うことでUserDefaultsKeyを抽象化し、キーを扱いやすくしています。
おわりに
おわりです。