Skip to content
beginner Phase 6 · Data Persistence

UserDefaults

Store lightweight preferences and settings with UserDefaults and property wrappers.

30m
2 problems
Topic Progress 0%

Reading & Writing

What is UserDefaults?

UserDefaults is a simple key-value store for small amounts of user preferences. It persists data across app launches but is NOT encrypted.

Writing Values

let defaults = UserDefaults.standard

defaults.set("Alice", forKey: "username")
defaults.set(42, forKey: "launchCount")
defaults.set(true, forKey: "hasCompletedOnboarding")
defaults.set(Date(), forKey: "lastLoginDate")

Reading Values

let username = defaults.string(forKey: "username")
let launchCount = defaults.integer(forKey: "launchCount")
let hasOnboarded = defaults.bool(forKey: "hasCompletedOnboarding")
let lastLogin = defaults.object(forKey: "lastLoginDate") as? Date

Removing Values

defaults.removeObject(forKey: "username")

Register Defaults

Set default values for first launch:

defaults.register(defaults: [
    "username": "Guest",
    "launchCount": 0,
    "hasCompletedOnboarding": false,
    "theme": "system"
])

This does not write to disk until a value is explicitly set.

What to Store

Store only small, non-sensitive data:

  • User preferences (theme, language)
  • Feature flags
  • Launch count
  • Last run version

Do NOT store:

  • Sensitive data (use Keychain)
  • Large data (use file system)
  • Complex objects (use Codable + FileManager)

Property Wrappers

@AppStorage for SwiftUI

@AppStorage provides a clean property wrapper for UserDefaults in SwiftUI:

struct SettingsView: View {
    @AppStorage("theme") private var theme = "system"
    @AppStorage("notificationsEnabled") private var notifications = true
    @AppStorage("fontSize") private var fontSize = 16.0

    var body: some View {
        Form {
            Picker("Theme", selection: $theme) {
                Text("Light").tag("light")
                Text("Dark").tag("dark")
                Text("System").tag("system")
            }

            Toggle("Notifications", isOn: $notifications)

            Slider(value: $fontSize, in: 12...24, step: 1)
        }
    }
}

@AppStorage vs @State

  • @State: In-memory, lost when view recreates
  • @AppStorage: Persists in UserDefaults, shared across the app

@AppStorage with Custom Defaults

Specify a custom UserDefaults suite:

@AppStorage("color", store: UserDefaults(suiteName: "com.app.group"))
private var color = "blue"

This is useful for App Groups where multiple extensions share data.

App Groups

Share UserDefaults between app and extensions:

let sharedDefaults = UserDefaults(suiteName: "group.com.app.shared")
sharedDefaults?.set("value", forKey: "key")

Combining with Observable

@Observable
class UserPreferences {
    var theme: String {
        get { UserDefaults.standard.string(forKey: "theme") ?? "system" }
        set { UserDefaults.standard.set(newValue, forKey: "theme") }
    }

    var notificationsEnabled: Bool {
        get { UserDefaults.standard.bool(forKey: "notificationsEnabled") }
        set { UserDefaults.standard.set(newValue, forKey: "notificationsEnabled") }
    }
}

Best Practices

Keep It Small

UserDefaults is not a database. Store only preferences and settings, not application data.

Thread Safety

UserDefaults is thread-safe for individual reads and writes. However, batch operations should be performed on a single thread.

Synchronize

defaults.synchronize()

This is rarely needed in modern iOS as UserDefaults auto-saves, but can be useful before app termination.

Testing

Use a custom suite for testing to avoid polluting shared defaults:

let testDefaults = UserDefaults(suiteName: "TestSuite")!
testDefaults.removePersistentDomain(forName: "TestSuite")

Migration

When changing default values, handle migrations:

func migrateDefaults() {
    let version = defaults.integer(forKey: "dataVersion")
    if version < 2 {
        // Migrate from v1 to v2
        defaults.set(2, forKey: "dataVersion")
    }
}

Security

UserDefaults stores data in plaintext plist files. Never store sensitive information like passwords, tokens, or personal data. Use Keychain for that purpose.

Quiz

1. What is UserDefaults designed for?

Question 1 options

2. What SwiftUI property wrapper binds to UserDefaults?

Question 2 options

3. Should you store passwords in UserDefaults?

Question 3 options

4. What does register(defaults:) do?

Question 4 options

Flashcards

Question

What is UserDefaults?

Answer

A simple key-value store for small user preferences. Stores plaintext plist files, not encrypted.

Question

How do you bind UserDefaults in SwiftUI?

Answer

@AppStorage provides two-way property binding to UserDefaults values.

Question

When should you NOT use UserDefaults?

Answer

For sensitive data (use Keychain), large data (use FileManager), or complex objects.

Question

What does register(defaults:) do?

Answer

Sets fallback values that are returned before any value is explicitly written to UserDefaults.

Revision Notes

Key Takeaways

  • 1. UserDefaults is for small, non-sensitive user preferences only
  • 2. @AppStorage provides clean SwiftUI integration with UserDefaults
  • 3. register(defaults:) sets fallback values before explicit writes
  • 4. Never store sensitive data in UserDefaults - use Keychain instead
  • 5. App Groups enable UserDefaults sharing between app and extensions

Interview Tips

  • Explain when to use UserDefaults vs Keychain vs Core Data
  • Know how @AppStorage simplifies SwiftUI UserDefaults binding
  • Discuss thread safety and synchronization of UserDefaults
  • Describe App Groups for shared UserDefaults between targets

Cheat Sheet

UserDefaults: Key-value store for small preferences. .set() and .string(forKey:) for read/write. @AppStorage for SwiftUI binding. register(defaults:) for fallback values. Not encrypted - use Keychain for sensitive data. Not a database - use SwiftData/CoreData for complex data.