import Foundation
enum UserDefaultsKey: String {
case username
case isFirstLaunch
case lastSyncDate
case theme
case fontSize
case notificationsEnabled
case selectedLanguage
}
class UserDefaultsManager {
static let shared = UserDefaultsManager()
private let defaults = UserDefaults.standard
private init() {}
// MARK: - Basic Types
func set<T>(_ value: T, forKey key: UserDefaultsKey) {
defaults.set(value, forKey: key.rawValue)
}
func get<T>(forKey key: UserDefaultsKey) -> T? {
return defaults.object(forKey: key.rawValue) as? T
}
func remove(forKey key: UserDefaultsKey) {
defaults.removeObject(forKey: key.rawValue)
}
// MARK: - Convenience Properties
var username: String? {
get { get(forKey: .username) }
set { set(newValue, forKey: .username) }
}
var isFirstLaunch: Bool {
get { get(forKey: .isFirstLaunch) ?? true }
set { set(newValue, forKey: .isFirstLaunch) }
}
var lastSyncDate: Date? {
get { get(forKey: .lastSyncDate) }
set { set(newValue, forKey: .lastSyncDate) }
}
// MARK: - Codable Support
func setCodable<T: Codable>(_ value: T, forKey key: String) {
if let encoded = try? JSONEncoder().encode(value) {
defaults.set(encoded, forKey: key)
}
}
func getCodable<T: Codable>(forKey key: String) -> T? {
guard let data = defaults.data(forKey: key) else { return nil }
return try? JSONDecoder().decode(T.self, from: data)
}
// MARK: - App Group Support
static func groupDefaults(suiteName: String) -> UserDefaults? {
return UserDefaults(suiteName: suiteName)
}
}
// MARK: - Type-safe wrapper
struct AppSettings: Codable {
var theme: String
var fontSize: Double
var notificationsEnabled: Bool
var selectedLanguage: String
static func load() -> AppSettings {
UserDefaultsManager.shared.getCodable(forKey: "appSettings") ?? AppSettings.default
}
func save() {
UserDefaultsManager.shared.setCodable(self, forKey: "appSettings")
}
static var `default`: AppSettings {
AppSettings(
theme: "system",
fontSize: 16,
notificationsEnabled: true,
selectedLanguage: "en"
)
}
}
UserDefaults provides simple key-value storage for app preferences and settings. It persists basic types like strings, numbers, bools, dates, and data automatically. I use UserDefaults for user preferences, app state, and feature flags—never for sensitive data like passwords. The standard suite UserDefaults.standard is most common. App groups share defaults between apps and extensions with UserDefaults(suiteName:). SwiftUI's @AppStorage property wrapper creates reactive bindings to UserDefaults. I create type-safe wrappers with enums for keys to prevent typos. For complex objects, I encode to Data with Codable. UserDefaults syncs to disk periodically, making it reliable for non-critical data.