UserDefaults - defaults...? I made this generic class which helps me reading values from user defaults. Besides it abusing several UserDefaults... what do you see? Can I make some instances share a reference?

class DefaultValue&lt;V&gt; {
    var userDefaults = UserDefaults()
    let name : String
    let defValue : V

    init(name: String, defValue: V) {
        self.name = name
        self.defValue = defValue
    }

    func get() -&gt; V {
        if userDefaults.object(forKey: name) == nil {
            return defValue
        }
        switch defValue {
        case is Int:
            return userDefaults.integer(forKey: name) as! V
        case is Bool:
            return userDefaults.bool(forKey: name) as! V
        case is String:
            return userDefaults.string(forKey: name) as! V
        default:
            return defValue
        }
    }

    func set(value: V) {
        userDefaults.set(value, forKey: name)
    }

    func reset() {
        userDefaults.set(defValue, forKey: name)
    }
}


Usage:

class MyDefaults {
    let LOGGED_IN = DefaultValue&lt;Bool&gt; (name: "logged_in", defValue: false)
    let USERNAME = DefaultValue&lt;String&gt; (name: "username", defValue: "")
}

var settings = MyDefaults()
if settings.LOGGED_IN.get() {
    settings.LOGGED_IN.set(value: true)
} else {
    settings.LOGGED_IN.reset()
}
The forced casts as! V in the get() method might seem safe at first
sight, since you do some type-checking before. But they are not:
If a default value for some key exists, but is of the wrong type then
your program can crash. This can for example happen if you change the
type of some default value between program releases. You can easily
reproduce the problem by setting the default value to an incompatible
type before retrieving it with your class:

UserDefaults.standard.set([1, 2, 3], forKey: "username")

class MyDefaults {
    let USERNAME = DefaultValue&lt;String&gt; (name: "username", defValue: "Joe")
}

print(MyDefaults.USERNAME.get())


which crashes at

return userDefaults.string(forKey: name) as! V
// Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value


This can be avoided with optional casting as? V, and then the
entire get() method simplifies to

func get() -&gt; V {
    return userDefaults.object(forKey: name) as? V ?? defValue
}


As a bonus, the code now works not only with integers, booleans and
strings, but with all types (provided that they are “UserDefaults compatible”).



I do not see a reason to create separate UserDefaults instances,
you can share the standard instance:

let userDefaults = UserDefaults.standard




The reset() method could simply remove the value instead of setting
the default value:

func reset() {
    userDefaults.removeObject(forKey: name)
}


A possible advantage is that you can retrieve it with a different
default value later.



I would use “default” as argument label in the init method:

init(name: String, default defValue: V) {
    self.name = name
    self.defValue = defValue
}


That looks nicer when called

let USERNAME = DefaultValue&lt;String&gt; (name: "username", default: "")


and also mimics the subscript(_:default:) of Swift dictionaries.

The set() method should have an empty argument label

func set(_ value: V) {
    userDefaults.set(value, forKey: name)
}


like, for example, set(_:forKey:).



If your intention of class MyDefaults is to avoid global variables
and to provide a “namespace”: This can also be achieved with a
case-less enum: 

enum MyDefaults {
    static let USERNAME = DefaultValue&lt;String&gt;(name: "username", default: "")
}

MyDefaults.USERNAME.set("elcuco")
print(MyDefaults.USERNAME.get())


so that no instances have to be created.



Naming: According to the Swift API Design Guidelines:


  Names of types and protocols are UpperCamelCase. Everything else is lowerCamelCase


so it should be 

enum MyDefaults {
    static let loggedIn = DefaultValue&lt;Bool&gt; (name: "logged_in", default: false)
    static let userName = DefaultValue&lt;String&gt;(name: "username", default: "")
}


instead of LOGGED_IN and USERNAME.