-
-
Save andyyhope/b2fbd9cbbc48a7d9d56ca3feb55bc1f5 to your computer and use it in GitHub Desktop.
//: | |
//: UserDefaultable.swift | |
//: | |
//: Created by Andyy Hope on 18/08/2016. | |
//: Twitter: @andyyhope | |
//: Medium: Andyy Hope, https://medium.com/@AndyyHope | |
import Foundation | |
// MARK: - Key Namespaceable | |
protocol KeyNamespaceable { } | |
extension KeyNamespaceable { | |
private static func namespace(_ key: String) -> String { | |
return "\(Self.self).\(key)" | |
} | |
static func namespace<T: RawRepresentable>(_ key: T) -> String where T.RawValue == String { | |
return namespace(key.rawValue) | |
} | |
} | |
// MARK: - Bool Defaults | |
protocol BoolUserDefaultable : KeyNamespaceable { | |
associatedtype BoolDefaultKey : RawRepresentable | |
} | |
extension BoolUserDefaultable where BoolDefaultKey.RawValue == String { | |
// Set | |
static func set(_ bool: Bool, forKey key: BoolDefaultKey) { | |
let key = namespace(key) | |
UserDefaults.standard.set(bool, forKey: key) | |
} | |
// Get | |
static func bool(forKey key: BoolDefaultKey) -> Bool { | |
let key = namespace(key) | |
return UserDefaults.standard.bool(forKey: key) | |
} | |
} | |
// MARK: - Float Defaults | |
protocol FloatUserDefaultable : KeyNamespaceable { | |
associatedtype FloatDefaultKey : RawRepresentable | |
} | |
extension FloatUserDefaultable where FloatDefaultKey.RawValue == String { | |
// Set | |
static func set(_ float: Float, forKey key: FloatDefaultKey) { | |
let key = namespace(key) | |
UserDefaults.standard.set(float, forKey: key) | |
} | |
// Get | |
static func float(forKey key: FloatDefaultKey) -> Float { | |
let key = namespace(key) | |
return UserDefaults.standard.float(forKey: key) | |
} | |
} | |
// MARK: - Integer Defaults | |
protocol IntegerUserDefaultable : KeyNamespaceable { | |
associatedtype IntegerDefaultKey : RawRepresentable | |
} | |
extension IntegerUserDefaultable where IntegerDefaultKey.RawValue == String { | |
// Set | |
static func set(_ integer: Int, forKey key: IntegerDefaultKey) { | |
let key = namespace(key) | |
UserDefaults.standard.set(integer, forKey: key) | |
} | |
// Get | |
static func integer(forKey key: IntegerDefaultKey) -> Int { | |
let key = namespace(key) | |
return UserDefaults.standard.integer(forKey: key) | |
} | |
} | |
// MARK: - Object Defaults | |
protocol ObjectUserDefaultable : KeyNamespaceable { | |
associatedtype ObjectDefaultKey : RawRepresentable | |
} | |
extension ObjectUserDefaultable where ObjectDefaultKey.RawValue == String { | |
// Set | |
static func set(_ object: AnyObject, forKey key: ObjectDefaultKey) { | |
let key = namespace(key) | |
UserDefaults.standard.set(object, forKey: key) | |
} | |
// Get | |
static func object(forKey key: ObjectDefaultKey) -> Any? { | |
let key = namespace(key) | |
return UserDefaults.standard.object(forKey: key) | |
} | |
} | |
// MARK: - Double Defaults | |
protocol DoubleUserDefaultable : KeyNamespaceable { | |
associatedtype DoubleDefaultKey : RawRepresentable | |
} | |
extension DoubleUserDefaultable where DoubleDefaultKey.RawValue == String { | |
// Set | |
static func set(_ double: Double, forKey key: DoubleDefaultKey) { | |
let key = namespace(key) | |
UserDefaults.standard.set(double, forKey: key) | |
} | |
// Get | |
static func double(forKey key: DoubleDefaultKey) -> Double { | |
let key = namespace(key) | |
return UserDefaults.standard.double(forKey: key) | |
} | |
} | |
// MARK: - URL Defaults | |
protocol URLUserDefaultable : KeyNamespaceable { | |
associatedtype URLDefaultKey : RawRepresentable | |
} | |
extension URLUserDefaultable where URLDefaultKey.RawValue == String { | |
// Set | |
static func set(_ url: URL, forKey key: URLDefaultKey) { | |
let key = namespace(key) | |
UserDefaults.standard.set(url, forKey: key) | |
} | |
// Get | |
static func url(forKey key: URLDefaultKey) -> URL? { | |
let key = namespace(key) | |
return UserDefaults.standard.url(forKey: key) | |
} | |
} | |
// MARK: - Use | |
// Preparation | |
extension UserDefaults { | |
struct Account : BoolUserDefaultable { | |
private init() { } | |
enum BoolDefaultKey : String { | |
case isUserLoggedIn | |
} | |
} | |
} | |
// Set | |
UserDefaults.Account.set(true, forKey: .isUserLoggedIn) | |
// Get | |
let isUserLoggedIn = UserDefaults.Account.bool(forKey: .isUserLoggedIn) |
@ServusJon, you can do it easily the only thing you have to remember is that the enum
name inside your struct
need to be the same as you define for your associatedtype
to conform the any of the protocols defined above, for example something like this:
extension UserDefaults {
struct BankAccount : IntegerUserDefaultable {
private init() { }
enum IntegerDefaultKey : String {
case moneyInTheAccount
}
}
}
And then you can call it like the following example:
// Set
UserDefaults.BankAccount.set(3, forKey: .moneyInTheAccount)
// Get
let moneyInTheAccount = UserDefaults.BankAccount.integer(forKey: .moneyInTheAccount) // 3
can you help me out in setting string variable in the UserDefault
Hi, nice gist ...just wanna ask why you separate each type to different protocol ?? we can just put it together like this
protocol KeyNamespaceable { }
extension KeyNamespaceable {
private static func namespace(_ key: String) -> String {
return "\(Self.self).\(key)"
}
static func namespace<T: RawRepresentable>(_ key: T) -> String where T.RawValue == String {
return namespace(key.rawValue)
}
}
protocol AccountDefaultable: KeyNamespaceable {
associatedtype AccountDefaultKey: RawRepresentable
}
extension AccountDefaultable where AccountDefaultKey.RawValue == String {
static func set(_ string: String, forKey key: AccountDefaultKey) {
UserDefaults.standard.set(string, forKey: namespace(key))
}
static func string(forKey key: AccountDefaultKey) -> String? {
return UserDefaults.standard.string(forKey: namespace(key))
}
static func set(_ integer: Int, forKey key: AccountDefaultKey) {
UserDefaults.standard.set(integer, forKey: namespace(key))
}
static func integer(forKey key: AccountDefaultKey) -> Int {
return UserDefaults.standard.integer(forKey: namespace(key))
}
}
extension UserDefaults {
struct Account: AccountDefaultable {
private init() { }
enum AccountDefaultKey: String {
case firstName
case lastName
}
}
}
if we separate with type, we cant set user default data with different type in one struct that have the enum ?? like age where age is integer not same with firstName and lastName
extension UserDefaults {
struct Account: AccountDefaultable {
private init() { }
enum AccountDefaultKey: String {
case firstName
case lastName
case age
}
}
}
thanks for the explaination
This is really cool!
@andyyhope how do you get the keypath to use in UserDefaults.standard.observe?
I tried to add a new struct to userdefaults but I always get errors that the name doesn't conform to the protocol. Could you please extend your example with an integer (struct)?