Skip to main content
Swift for iOS Development
CHAPTER 20 Beginner

Local Storage with UserDefaults

Updated: May 16, 2026
7 min read

# CHAPTER 20

Local Storage with UserDefaults

1. Introduction

If a user flips a "Dark Mode" switch in your app, closes the app completely, and opens it again tomorrow, they expect it to still be in Dark Mode. If you rely solely on @State, that data is destroyed the millisecond the app closes. We need a way to save tiny pieces of information permanently to the physical hard drive of the iPhone. In this chapter, we will master Local Storage with UserDefaults. We will learn the legacy UserDefaults API for complex logic, and the incredibly powerful, modern @AppStorage wrapper specifically designed for SwiftUI.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand the concept of persistent local storage.
  • Write and Read basic data types using the UserDefaults API.
  • Understand the specific limitations of what UserDefaults should hold.
  • Utilize the @AppStorage property wrapper for instant, reactive persistence in SwiftUI.

3. The UserDefaults API (The Core Engine)

UserDefaults is a simple database built into every iPhone app. It works like a Dictionary: you save data under a specific String "Key", and later, you ask the database for the data matching that Key.

Saving Data (Writing to disk):

swift
12345
// Saving a high score
UserDefaults.standard.set(500, forKey: "HighestScore")

// Saving a boolean
UserDefaults.standard.set(true, forKey: "IsProUser")

Retrieving Data (Reading from disk):

swift
123
// Retrieving the score later
let savedScore = UserDefaults.standard.integer(forKey: "HighestScore")
print(savedScore) // Prints 500

*Note: If the key doesn't exist yet, integer(forKey:) safely returns 0, and bool(forKey:) safely returns false.*

4. What SHOULD you save in UserDefaults?

UserDefaults is designed for tiny, simple data:
  • User Settings (Dark Mode, Notification preferences).
  • Feature flags (Has the user completed the onboarding tutorial?).
  • High scores or simple integers.

DO NOT save heavy data here!

  • Do not save massive arrays of downloaded JSON.
  • Do not save Images.
  • Do not save highly sensitive passwords (use the iOS Keychain instead).
Saving massive files in UserDefaults will significantly slow down the launch time of your application!

5. The SwiftUI Magic: @AppStorage

While UserDefaults.standard is great for ViewModels, Apple introduced a magical wrapper for SwiftUI Views called @AppStorage. It replaces @State. It acts exactly like @State (triggering UI redraws when changed), BUT it instantly saves the value to the hard drive behind the scenes!
swift
123456789101112131415
import SwiftUI

struct SettingsView: View {
    // 1. We specify the "Key" we want to save it under in the database.
    // 2. We provide a default value (false) if the user has never saved anything.
    @AppStorage("isDarkModeOn") private var isDarkMode = false
    
    var body: some View {
        Form {
            // When the user toggles this, it changes the variable, redraws the UI, 
            // AND saves the new boolean to the iPhone hard drive permanently!
            Toggle("Enable Dark Mode", isOn: $isDarkMode)
        }
    }
}

6. Mini Project: Persistent Theme Settings

Let's build an app where the background color permanently remembers the user's choice.
swift
123456789101112131415161718192021222324252627282930313233343536373839
import SwiftUI

struct ThemeAppView: View {
    // This is connected directly to the hard drive!
    @AppStorage("themeColor") private var selectedColorString = "Blue"
    
    // A computed property to turn the saved String into an actual SwiftUI Color!
    var themeColor: Color {
        switch selectedColorString {
        case "Red": return .red
        case "Green": return .green
        default: return .blue
        }
    }
    
    var body: some View {
        ZStack {
            themeColor.ignoresSafeArea() // The background reacts to the saved setting!
            
            VStack {
                Text("Select your Theme")
                    .font(.title)
                    .foregroundColor(.white)
                    .padding()
                
                // Changing the Picker immediately saves the new String to the hard drive!
                Picker("Theme Color", selection: $selectedColorString) {
                    Text("Blue Theme").tag("Blue")
                    Text("Red Theme").tag("Red")
                    Text("Green Theme").tag("Green")
                }
                .pickerStyle(.segmented)
                .padding()
                .background(Color.white.opacity(0.8))
                .cornerRadius(10)
            }
        }
    }
}

*If you close this app and open it a year later, the background will still be Green!*

7. Combining UserDefaults and ViewModels

If you are strictly following MVVM architecture, your @AppStorage logic might need to live inside your ObservableObject brain instead of the View. You can use UserDefaults.standard directly inside your ViewModel logic!
swift
1234567891011121314
class ProfileViewModel: ObservableObject {
    @Published var score = 0
    
    init() {
        // When the brain boots up, fetch the saved score from the hard drive!
        self.score = UserDefaults.standard.integer(forKey: "UserScore")
    }
    
    func increaseScore() {
        score += 10
        // Instantly save it to the hard drive!
        UserDefaults.standard.set(score, forKey: "UserScore")
    }
}

8. Common Mistakes

  • Typo in the Key: If you save data using the key "HighScore", but later try to read it using "highScore" (lowercase 'h'), you will get 0. The keys are strictly case-sensitive Strings. A professional practice is to define all keys as static constants in a central file to prevent typos.
  • Saving Structs: You cannot directly save a custom Swift struct (like User) into UserDefaults. It only accepts basic types (String, Int, Double, Bool, Data). To save a Struct, you must first encode it into JSON Data using JSONEncoder(), which is generally overkill for UserDefaults.

9. Best Practices

  • Use @AppStorage for UI Toggles: If the persistent data is purely visual (like a toggle switch or a color preference), bypass the ViewModel entirely and use @AppStorage directly in the View. It requires 90% less code and is perfectly safe.

10. Exercises

  1. 1. Write the Swift code utilizing UserDefaults.standard to save the String "JohnDoe" under the key "username".
  1. 2. Create an @AppStorage variable named hasSeenOnboarding that defaults to false.

11. Coding Challenges

Challenge: Build an "App Launch Counter". Create an ObservableObject ViewModel. Inside its init(), read an integer from UserDefaults using the key "LaunchCount". Add 1 to it. Save the new number back to UserDefaults, and publish the number to a SwiftUI View displaying "You have opened this app X times!".

12. MCQ Quiz with Answers

Question 1

What is the primary architectural limitation regarding the type and size of data that should be stored within UserDefaults?

Question 2

In SwiftUI, which property wrapper acts identically to @State (triggering reactive UI redraws) but automatically persists the data to the device's local memory behind the scenes?

13. Interview Questions

  • Q: Explain the mechanical difference between a variable wrapped in @State versus one wrapped in @AppStorage. In the context of the application lifecycle, what happens to the data when the user forcibly terminates the app from the multitasking switcher?
  • Q: Why is the iOS Keychain fundamentally required for storing user passwords, rather than simply saving them as a String inside UserDefaults?
  • Q: Describe how a developer might mitigate string-typo errors when managing dozens of UserDefaults keys across a massive codebase.

14. FAQs

Q: How do I completely delete all saved data while testing my app? A: In the iOS Simulator, simply tap and hold the app icon on the home screen, select "Remove App", and delete it. When you click Play in Xcode again, it will install a fresh copy with a completely wiped UserDefaults database!

15. Summary

In Chapter 20, we conquered the limitations of ephemeral memory, enabling our applications to remember data across complete restarts. We explored the core UserDefaults API, establishing persistent key-value storage for simple logic flags within our ViewModels. We then embraced the modern elegance of SwiftUI by implementing the @AppStorage wrapper, creating visual interfaces that react to user input and silently save preferences to the physical hard drive in a single line of code.

16. Next Chapter Recommendation

UserDefaults is perfect for small toggles, but what if you are building an offline Notes app and need to save 1,000 highly complex documents locally? You need a real database. Proceed to Chapter 21: Core Data Database Basics.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·