How to implement persistent storage in swiftUI?

By | February 17, 2025

There are multiple ways to implement persistent storage in SwiftUI, including the key property wrappers for state management.

Let me explain the key concepts demonstrated in this implementation:

import SwiftUI
import Combine

// MARK: - Data Model
class UserSettings: ObservableObject {
    // @Published automatically notifies observers when value changes
    @Published var username: String {
        didSet {
            // Save to UserDefaults whenever value changes
            UserDefaults.standard.set(username, forKey: "username")
        }
    }
    
    @Published var isDarkMode: Bool {
        didSet {
            UserDefaults.standard.set(isDarkMode, forKey: "isDarkMode")
        }
    }
    
    // Initialize with stored values or defaults
    init() {
        self.username = UserDefaults.standard.string(forKey: "username") ?? ""
        self.isDarkMode = UserDefaults.standard.bool(forKey: "isDarkMode")
    }
}

// MARK: - Main App
@main
struct PersistentStorageApp: App {
    // Create a single instance to be shared across the app
    @StateObject private var userSettings = UserSettings()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(userSettings)
        }
    }
}

// MARK: - Content View
struct ContentView: View {
    // Inject the shared UserSettings instance
    @EnvironmentObject var userSettings: UserSettings
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("User Settings")) {
                    TextField("Username", text: $userSettings.username)
                    Toggle("Dark Mode", isOn: $userSettings.isDarkMode)
                }
                
                Section {
                    NavigationLink("Profile View") {
                        ProfileView()
                    }
                }
            }
            .navigationTitle("Settings")
        }
    }
}

// MARK: - Profile View
struct ProfileView: View {
    @EnvironmentObject var userSettings: UserSettings
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Welcome, \(userSettings.username)!")
            Text("Theme: \(userSettings.isDarkMode ? "Dark" : "Light")")
        }
    }
}
1. @ObservableObject & @Published
  • ObservableObject is a protocol that allows a class to publish changes to its properties
  • @Published is a property wrapper that automatically notifies observers when the value changes
  • In our example, UserSettings class uses these to notify views when data changes
2. @StateObject & @EnvironmentObject
  • @StateObject is used to create and maintain a single instance of an observable object
  • @EnvironmentObject injects shared data into views through the environment
  • The main app creates the UserSettings instance with @StateObject
  • Child views access it using @EnvironmentObject
3. Persistent Storage Implementation
  • UserDefaults is used for simple persistent storage
  • Changes are automatically saved when @Published properties are modified
  • Values are loaded from storage during initialization
4. Data Flow
  • Changes in any view are automatically:
    1. Saved to UserDefaults
    2. Published to all observing views
    3. UI updates automatically reflect the changes

This implementation provides several benefits:

  • Automatic persistence
  • Shared state across views
  • Clean separation of concerns
  • Automatic UI updates

Thank you for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *