Last active
November 15, 2020 10:27
-
-
Save andyyhope/b2fbd9cbbc48a7d9d56ca3feb55bc1f5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: | |
//: 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) |
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?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@ServusJon, you can do it easily the only thing you have to remember is that the
enum
name inside yourstruct
need to be the same as you define for yourassociatedtype
to conform the any of the protocols defined above, for example something like this:And then you can call it like the following example: