100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Swift

UserDefaults and Persistence

Learn when and how to use UserDefaults for lightweight app settings and small persisted values, and how it compares to other persistence options.

Data & NetworkingBeginner7 min readJul 8, 2026
Analogies

UserDefaults and Persistence

UserDefaults is a simple key-value store backed by a property-list file on disk, designed for small amounts of app state that need to survive relaunches — things like a user's preferred theme, whether onboarding has been completed, or the last-selected tab. It is not a database: values are loaded into memory in their entirety when the app starts, so storing large objects or collections in UserDefaults degrades performance and is explicitly discouraged by Apple. For structured or large datasets, SwiftData, Core Data, or a file on disk are the appropriate tools; UserDefaults is reserved for genuinely small, simple state.

🏏

Cricket analogy: A scorer jotting the current match's toss result and chosen ends on a small notepad is fine, but trying to log every ball of a five-day Test on that same notepad would be absurd—just as UserDefaults suits small state but not large datasets.

Reading and Writing Values

UserDefaults.standard is the shared defaults database for your app. It provides typed accessors like bool(forKey:), integer(forKey:), string(forKey:), and a generic object(forKey:), alongside set(_:forKey:) for writing. Values are stored using property-list-compatible types: String, Int, Double, Bool, Date, Data, Array, and Dictionary of those. Writes are synchronized to disk automatically by the system; you no longer need to call synchronize(), which is deprecated because the system already persists changes at appropriate times.

🏏

Cricket analogy: A scorebook has clearly labeled slots for runs (a number), wickets (a number), and the batsman's name (text), each written and read in its own typed column, mirroring UserDefaults' typed accessors like integer(forKey:) and string(forKey:).

swift
enum SettingsKeys {
    static let hasCompletedOnboarding = "hasCompletedOnboarding"
    static let preferredThemeRawValue = "preferredTheme"
}

func completeOnboarding() {
    UserDefaults.standard.set(true, forKey: SettingsKeys.hasCompletedOnboarding)
}

func loadOnboardingState() -> Bool {
    UserDefaults.standard.bool(forKey: SettingsKeys.hasCompletedOnboarding)
}

SwiftUI Integration with @AppStorage

SwiftUI provides @AppStorage, a property wrapper that reads and writes a UserDefaults value while also triggering a view update whenever that value changes — behaving like @State but backed by persistent storage. It supports the same primitive types UserDefaults supports natively, plus any RawRepresentable enum whose raw value is String or Int, which is convenient for storing enum-based settings like a theme preference directly.

🏏

Cricket analogy: A live scoreboard that auto-updates the moment the official scorer changes the run total, without anyone manually refreshing the display, mirrors @AppStorage triggering a view update the instant its backing UserDefaults value changes.

swift
enum Theme: String {
    case light, dark, system
}

struct SettingsView: View {
    @AppStorage("preferredTheme") private var themeRawValue: String = Theme.system.rawValue

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

Storing Codable Values

UserDefaults doesn't natively store arbitrary Codable structs, but you can bridge them by encoding to Data with JSONEncoder before writing, and decoding back with JSONDecoder after reading. This is a reasonable pattern for a small settings object, but if the data grows into a real collection — a list of downloaded items, a user's saved articles — that's a signal to move to SwiftData or a dedicated file rather than continuing to stretch UserDefaults.

🏏

Cricket analogy: Jotting a fielding-position preference as a coded note works for one small setting, but once you're tracking an entire growing list of a bowler's dismissals across a season, it's time to move from sticky notes to a proper scorebook database, mirroring the shift from UserDefaults to SwiftData.

UserDefaults is not encrypted and should never be used to store sensitive data like passwords or auth tokens — the Keychain is the correct, encrypted storage location for secrets on Apple platforms.

Because UserDefaults loads its entire contents into memory at process launch, storing large arrays, images, or big Codable blobs in it can noticeably slow app launch. Treat it strictly as a store for small, simple preferences.

  • UserDefaults is a lightweight, property-list-backed key-value store intended for small settings, not large datasets.
  • UserDefaults.standard offers typed accessors (bool, integer, string, object) for reading and writing.
  • @AppStorage binds a UserDefaults key directly into SwiftUI state, triggering view updates on change.
  • Codable structs can be bridged into UserDefaults by encoding to Data, but this doesn't scale to large collections.
  • Sensitive data (passwords, tokens) belongs in the Keychain, never in UserDefaults, since it is unencrypted.
  • synchronize() is deprecated; the system persists UserDefaults writes automatically.

Practice what you learned

Was this page helpful?

Topics covered

#Swift#IOSWithSwiftUIStudyNotes#MobileDevelopment#UserDefaultsAndPersistence#UserDefaults#Persistence#Reading#Writing#StudyNotes#SkillVeris