UserDefaults
UserDefaults
is a lightweight, built-in mechanism in iOS for storing small amounts of user data, such as preferences or settings, persistently across app launches. It provides a simple key-value storage system, ideal for saving basic data types like booleans, strings, numbers, and property list objects. This document covers the usage of UserDefaults
in UIKit-based iOS applications.
Purpose
- Persistent Storage: Save user preferences or app state between sessions.
- Simple Interface: Store and retrieve key-value pairs without complex setup.
- Cross-Session Access: Access data globally within the app or across apps in the same app group.
Key Features
- Standard Instance:
UserDefaults.standard
provides the default storage for the app. - Supported Types: Stores property list types (
String
,Int
,Double
,Bool
,Data
,Array
,Dictionary
) andDate
. - App Groups: Supports shared storage for apps and extensions via app groups.
- Synchronization: Automatically or manually synchronizes data to disk.
Basic Usage
UserDefaults
is accessed via the UserDefaults
class, typically using the standard
instance. Data is stored and retrieved using keys.
Example: Storing and Retrieving Data
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Store data
let defaults = UserDefaults.standard
defaults.set("John Doe", forKey: "username")
defaults.set(true, forKey: "isDarkModeEnabled")
defaults.set(42, forKey: "userScore")
// Retrieve data
if let username = defaults.string(forKey: "username") {
print("Username: \(username)")
}
let isDarkMode = defaults.bool(forKey: "isDarkModeEnabled")
print("Dark Mode: \(isDarkMode)")
let score = defaults.integer(forKey: "userScore")
print("Score: \(score)")
}
}
Key Points
- Setting Values: Use
set(_:forKey:)
to store supported types. - Retrieving Values: Use type-specific methods like
string(forKey:)
,bool(forKey:)
,integer(forKey:)
, orobject(forKey:)
for generic access. - Default Values: Methods like
string(forKey:)
returnnil
if the key doesn’t exist; use optional binding or default values. - Keys: Use unique, descriptive strings to avoid conflicts.
Advanced Features
Storing Complex Data
UserDefaults
supports property list-compatible collections (Array
, Dictionary
) and Data
for custom objects (via serialization).
Example: Storing an Array
let defaults = UserDefaults.standard
let scores = [100, 200, 300]
defaults.set(scores, forKey: "highScores")
if let retrievedScores = defaults.array(forKey: "highScores") as? [Int] {
print("High Scores: \(retrievedScores)")
}
Example: Storing Custom Objects
Custom objects must be serialized to Data
using NSKeyedArchiver
or JSON encoding.
struct User: Codable {
let name: String
let age: Int
}
let user = User(name: "Jane", age: 30)
if let encodedData = try? JSONEncoder().encode(user) {
UserDefaults.standard.set(encodedData, forKey: "userData")
}
if let data = UserDefaults.standard.data(forKey: "userData"),
let decodedUser = try? JSONDecoder().decode(User.self, from: data) {
print("User: \(decodedUser.name), Age: \(decodedUser.age)")
}
Synchronization
UserDefaults
automatically saves changes periodically, but you can force synchronization:
UserDefaults.standard.synchronize() // Rarely needed in modern iOS
Note: synchronize()
is largely deprecated as iOS handles saving automatically, but it can be used for immediate persistence in rare cases.
App Groups
For shared data between an app and its extensions, use an app group:
if let groupDefaults = UserDefaults(suiteName: "group.com.example.app") {
groupDefaults.set("Shared Data", forKey: "sharedKey")
if let value = groupDefaults.string(forKey: "sharedKey") {
print("Shared: \(value)")
}
}
Configure the app group in Xcode’s “Capabilities” for both the app and extension.
Removing Data
Remove specific keys or all data:
UserDefaults.standard.removeObject(forKey: "username")
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!) // Reset all
Observing Changes
Use KVO (Key-Value Observing) or notifications to monitor changes:
NotificationCenter.default.addObserver(
forName: UserDefaults.didChangeNotification,
object: nil,
queue: .main
) { _ in
print("UserDefaults changed")
}
Best Practices
- Small Data: Use
UserDefaults
for small, simple data (e.g., settings, flags); use Core Data or file storage for large datasets. - Key Naming: Use clear, namespaced keys (e.g.,
com.example.setting
) to avoid conflicts. - Type Safety: Use type-specific retrieval methods to avoid casting errors.
- App Groups: Enable app groups for shared data in extensions or watchOS apps.
- Performance: Avoid frequent writes to
UserDefaults
in performance-critical code. - Security: Do not store sensitive data (e.g., passwords) in
UserDefaults
; use Keychain instead. - Testing: Verify data persistence across app launches and device restarts.
Limitations
- Data Types: Only property list types and
Data
are supported natively. - Size: Not suitable for large data (e.g., images or large datasets).
- Security: Data is stored unencrypted; use Keychain for sensitive information.
- Thread Safety:
UserDefaults
is thread-safe, but avoid heavy operations on the main thread.
Summary
UserDefaults
provides a simple, efficient way to store small amounts of persistent data in UIKit apps. It supports basic types, collections, and serialized custom objects, with features like app groups for shared storage. While ideal for user preferences, it’s not suited for large or sensitive data. By following best practices, developers can leverage UserDefaults
to create seamless, persistent user experiences.