Emin Grbo, May 13 2020

Saving objects to UserDefaults

UserDefaults are not meant to hold huge amount of data or complex objects. We use CoreData for that. But hey, sometimes we are just that lazy

Recently a twitter friend of mine Dan O'Leary asked how to save objects to UserDefaults and he seemed to like this extension, so I thought I could share it with all you awesome folks!

Did you know he has an app on the store as well? Check it out -> RunRooster

So, let's not dillydally and get right into it. Quiet in the back! 🤫


Preparation 🧩

First, let's create the object that we will be saving. A simple object you might use in the app.

struct UserSettings: Codable {
    var userID: Int?
    var modePreference: String?
    var volumeLevel: Int?
    var highScore: Int?
    
    enum CodingKeys: String, CodingKey {
        case userID = "id"
        case modePreference = "mode_preference"
        case volumeLevel = "volume_level"
        case highScore = "highscore"
    }
}

I added those CodingKeys as a bonus in case you are getting that info from the server as well. And snake_case is soooo last year. Ama right?

Now we got ourselves a nice UserSettings where we could hold on to some user....settings? 🥁

This is really useful if user changes some info but does not have a wi-fi or any other type of internet connection, so you can hold on to this object and send it at a later date.


Next, and this is not obligatory, we will create a separate object that holds all our keys. I made a mistake of using hand-typed strings for keys once...and never again. (stuff of nightmares)

struct Keys {
    static let userSettings = "userSettings"
}

Execution 🤯🔫

Saving

Here comes the interesting bit. Let's create an extension that will save that object to UserDefaults.

extension UserDefaults {

static func saveUserSettings(_ settings: UserSettings) {
    do {
        let encodedUserSettings = try JSONEncoder().encode(settings)
        UserDefaults.standard.set(encodedUserSettings, forKey: Keys.userSettings)
        UserDefaults.standard.synchronize() // this line is a joke btw, read the summary, so awesome :) 
    } catch {
        print("Error saving User Settings.")
    }
}

//... loading code goes here

}

Thats it! Quite easy right? Now you know why some developers (myself included) really love to use this quick and dirty way of saving some small objects for later use.


Loading

Same as the above, but in reverse ⏪

//... saving code

static func loadUserSettings() -> UserSettings? {
    guard let settings = UserDefaults.standard.object(forKey: Keys.userSettings),
          let loadedSettings = settings as? Data else {
        return nil
    }
    return try? JSONDecoder().decode(UserSettings.self, from: loadedSettings)
}

The only difference is that here our return is optional. Maybe we still haven't saved anything, who knows 🤷‍♂️


All jokes regarding laziness aside, keep in mind that UserDefaults size DOES affect you app loading time, and should only be used for smaller objects. Also, saving a user password there is a giant NO-NO.

Hope that helps on your future coding endeavours!


Thank you for reading, you lovely person! ♥️ Lets hook up on Twitter?

Consider subscribing to the newsletter below. Cheers! 🍻

Tagged with: