Instantly share code, notes, and snippets.

Embed
What would you like to do?
//:
//: 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

This comment has been minimized.

ServusJon commented Nov 2, 2016

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)?

@Vkt0r

This comment has been minimized.

Vkt0r commented Nov 2, 2016

@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
@CodeLinersTech

This comment has been minimized.

CodeLinersTech commented Jun 7, 2017

can you help me out in setting string variable in the UserDefault

@alfian0

This comment has been minimized.

alfian0 commented Aug 27, 2017

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment