Skip to Content
Course content

74: UserDefaults for Simple Persistence

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've probably had that annoying experience where you close an app, reopen it, and find that it's completely forgotten who you are or how you liked your settings configured. For small pieces of data—like a username, a "Dark Mode" toggle, or a volume preference—you don't need a full-blown database. That's where UserDefaults comes in.

Think of UserDefaults as a simple key-value store. It's essentially a dictionary that the system saves to the disk for you. It isn't meant for large datasets, but for "preferences," it's the gold standard.

Saving a basic user profile

Let's build a tiny settings manager for a hypothetical reading app. I want to save the user's nickname so we can greet them when they return. In Swift, we access the shared defaults object through UserDefaults.standard.

let defaults = UserDefaults.standard
defaults.set("SwiftLearner", forKey: "username")

That's it. One line. The system takes that string and writes it to a plist file in the app's sandbox. I usually avoid creating a local variable for UserDefaults.standard if I'm only doing one thing, but since we're doing a few, it keeps the code cleaner.

Handling a theme toggle

Next, let's add a boolean to track whether the user prefers Dark Mode. Booleans are handled exactly the same way as strings.

defaults.set(true, forKey: "darkModeEnabled")

Now we have two pieces of data stored. But here is where things usually go sideways in a real project. Let me show you a mistake I actually made in a production app a few years ago.

The "Magic String" trap

I decided to read the theme preference back to apply the colors to my UI. I wrote this:

let isDark = defaults.bool(forKey: "dark_mode_enabled")
print("Is dark mode on? \(isDark)") 
// Output: Is dark mode on? false

Wait. I just set it to true. Why is it returning false? If you look closely, I used "darkModeEnabled" (camelCase) when saving, but "dark_mode_enabled" (snake_case) when reading. UserDefaults doesn't throw an error when you provide a key that doesn't exist; it just returns the default value for that type (which is false for Booleans and nil for Strings).

To stop this from happening, I never use "magic strings" directly in the set or bool methods. Instead, I'll use a private enum or a struct to manage my keys.

enum UserKey {
    static let username = "username"
    static let darkMode = "darkModeEnabled"
}

// Now it's impossible to have a typo mismatch
defaults.set(true, forKey: UserKey.darkMode)
let isDark = defaults.bool(forKey: UserKey.darkMode)

Retrieving the data on launch

Finally, we need to actually use these values when the app starts. Since string(forKey:) returns an optional (because the user might not have set a name yet), I'll use the nil-coalescing operator to provide a friendly fallback.

let savedName = UserDefaults.standard.string(forKey: UserKey.username) ?? "Guest"
let isDarkMode = UserDefaults.standard.bool(forKey: UserKey.darkMode)

print("Welcome back, \(savedName)!")
print("Applying \(isDarkMode ? "Dark" : "Light") theme.")

It's simple, it's fast, and it survives an app restart. Just remember: if you start finding yourself saving giant arrays of complex objects here, stop. That's when you move to File Storage or Core Data.




📋 Practical Task

Build a Persistent High Score Tracker

Create a small program that manages a player's high score for a game. Your code should do the following:

  • Define a ScoreKey struct or enum to avoid string typos.
  • Write a function saveHighScore(score: Int) that saves an integer to UserDefaults.
  • Write a function getHighScore() -> Int that retrieves the score.
  • In your main execution, save a score of 5000, then print it back to the console to verify it was stored correctly.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.