-1

tldr; why do we always use UserDefaults.standard instead of subclassing UserDefaults to make something that more precisely fits our needs?

Has anyone out there subclassed UserDefaults before? Or is that considered bad practice?

Say, for example, that we make a ColorDefaults subclass of UserDefaults. When the app, the ColorDefaults object is instantiated, and that object loads all its own data. And the loaded data can then by sent to an appropriate object via delegation, or made universally available via a singleton.

My running theory is that UserDefaults is only meant to store relatively amounts of data, so having to use a singleton enforces that idea.

Bottom line: do we use UserDefaults.standard because:

  1. subclassing is frowned upon
  2. we're supposed to avoid saving too much data to UserDefaults in general
  3. there's just not much value in subclassing anyway?
  4. pretty much anything else.
Ashish Kakkad
  • 23,586
  • 12
  • 103
  • 136
  • 1
    “Say, for example, that we make a ColorDefaults subclass of UserDefaults. When the app, the ColorDefaults object is instantiated, and that object loads all its own data” All of that makes sense except this being a subclass of UserDefaults. A string that reads itself from a file is a kind of string, not a kind of file. – matt Jun 13 '18 at 05:17
  • Sub-classing shouldn't be necessary; use a binding and a ready-made instance of the `unarchiveFromDataTransformerName` value transformer. – l'L'l Jun 13 '18 at 05:38

3 Answers3

3

Your ColorDefaults should not be a subclass of UserDefaults. It should be a plain struct or class with computed properties that are backed by UserDefaults.

Here is an example using static properties but you could refactor this to use a singleton class instead.

struct ColorDefaults {
    static var someDefault: String {
        get {
            return UserDefaults.standard.string(forKey: "someKey") ?? "some initial value"
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "someKey")
        }
    }
}

let someVal = ColorDefaults.someDefault // read
ColorDefaults.someDefault = "hello" // write

This would also be useful if one of your defaults was more complicated and needed to be encoded/decoded for UserDefaults. The logic goes in here and not all over your app.

Note that such a class should only be used to store small bits of preferences, not full blown app data.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
0

User defaults are a system of storage on file. There is little sense in subclassing unless you want to change some of its logic. But you can create multiple suits like UserDefaults(suiteName: String). What do you expect you would do with subclassing? You could simply just globally define let myDefaults = UserDefaults(suiteName: String) and use it anywhere. I guess you could use methods like

class MyDefaults: UserDefaults {

    func saveName(_ name: String) {
        setValue(name, forKey: "name_key")
    }

}

But then again it might make more sense to just create an extension

extension UserDefaults {
    func saveName(_ name: String) {
        setValue(name, forKey: "name_key")
    }
}

Or make it a bit more complex:

extension UserDefaults {

    struct User {
        static let defaults = UserDefaults(suiteName: "User")
        static func saveName(_ name: String) {
            defaults.setValue(name, forKey: "name")
        }
    }

    struct General {
        static let defaults = UserDefaults.standard
        static func saveLastOpened(date: Date) {
            defaults.setValue(date, forKey: "last_opened")
        }
    }

}

But all of these have one fatal flow: Now you are dependent on using user defaults within the app. At some point you may find the need to rather save these data in some other form like a local JSON file synced with iCloud. I guess UserDefaults.User could be modified to do so but would be very ugly. What we want is not UserDefaults.User.saveName("My name") but User.saveName("My name"). From the interface perspective we do not care where this user name is saved and if a new system is introduced to save these data we don't want the change in interface.

In other words, imagine you are using UserDefaults.User.saveName on 100 places in your application and now want to use another system for saving user name. You will now need to change your code on 100 places to use AnotherSystem.User.saveName while if you simply use User.saveName the interface is still valid.

So the bottom line is there is no sense in (extensively) modifying, extending or subclassing UserDefaults because it is better creating a system that wraps UserDefaults and may later be changed to any other system.

Matic Oblak
  • 16,318
  • 3
  • 24
  • 43
-1

Seems you are looking for something like this

class ColorDefaults : NSObject
{
    /// Save Data
    class func saveDataInDefaultForKey(_ key: String, _ data: Any){
        UserDefaults.standard.set(data, forKey: key)
    }

    /// Retrieve data
    class func retrieveDataFromDefaultsWithKey(_ key: String) -> Any {
        return UserDefaults.standard.value(forKey: key) as Any
    }
}

Save and get data:

/// Save Data
ColorDefaults.saveDataInDefaultForKey("myArray", myArray)
ColorDefaults.saveDataInDefaultForKey("myString", myString)

/// Get Data
if let valueString = ColorDefaults.retrieveDataFromDefaultsWithKey("myString") as? String {
        print("Saved Value String: \(valueString)")
 }
 else {
        print("Error retrieving myString")
 }

 if let valueArray = ColorDefaults.retrieveDataFromDefaultsWithKey("myArray") as? [String] {
        print("Saved Value Array: \(valueArray)")
 }
 else{
        print("Error retrieving myArray")
 }

Output:

enter image description here

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
iOS Geek
  • 4,825
  • 1
  • 9
  • 30
  • 1
    Why does `colorDefaults` extend `NSObject`? Why does is violate standard naming conventions? – rmaddy Jun 13 '18 at 05:36