In Swift, UserDefaults
provides a straightforward way to store small amounts of data that need to persist across app launches, such as user preferences or settings. It’s a great tool for saving simple data types like String
, Int
, Bool
, and even collections such as Array
and Dictionary
. In this blog, we’ll cover the basics of UserDefaults
, how to use it, best practices, and a guide on creating a UserDefaultsManager
to simplify your usage further.
What is UserDefaults ?
UserDefaults
is a class in the Foundation framework that enables you to save data between app sessions. It is primarily used for storing user-specific information, which remains accessible even if the app is closed or restarted. The data is stored in a property list file in the app’s sandbox, making it secure and lightweight.
Common Use Cases for UserDefaults
- User Preferences - e.g., storing theme preferences (light or dark mode)
- App Settings - e.g., enabling or disabling notifications
- Session Data - e.g., checking if the user has already seen an onboarding screen
- Simple Data Storage - e.g., last login date or selected language
Storing Data in UserDefaults
UserDefaults
can store basic data types, such as Bool
, Int
, Float
, Double
, String
, URL
, and even collections like Array
and Dictionary
.
Saving and Retrieving Simple Data Types
// Save a value
UserDefaults.standard.set(true, forKey: "isDarkModeEnabled")
UserDefaults.standard.set(24, forKey: "userAge")
UserDefaults.standard.set("John", forKey: "userName")
// Retrieve values
let isDarkModeEnabled = UserDefaults.standard.bool(forKey: "isDarkModeEnabled")
let userAge = UserDefaults.standard.integer(forKey: "userAge")
let userName = UserDefaults.standard.string(forKey: "userName") ?? "Guest"
Here:
- We save a
Bool
,Int
, andString
toUserDefaults
. - We then retrieve each value using its associated key.
Working with Collections
UserDefaults
also supports storing collections, such as arrays and dictionaries, as long as they contain supported types.
Saving and Retrieving Arrays and Dictionaries
// Save an array
UserDefaults.standard.set(["Apples", "Bananas", "Oranges"], forKey: "favoriteFruits")
// Save a dictionary
UserDefaults.standard.set(["firstName": "John", "lastName": "Doe"], forKey: "userProfile")
// Retrieve the array
if let favoriteFruits = UserDefaults.standard.array(forKey: "favoriteFruits") as? [String] {
print(favoriteFruits) // ["Apples", "Bananas", "Oranges"]
}
// Retrieve the dictionary
if let userProfile = UserDefaults.standard.dictionary(forKey: "userProfile") as? [String: String] {
print(userProfile) // ["firstName": "John", "lastName": "Doe"]
}
Deleting Data from UserDefaults
To delete data from UserDefaults
, use the removeObject(forKey:)
method, specifying the key of the value you wish to delete.
Removing Data
// Remove specific value
UserDefaults.standard.removeObject(forKey: "userAge")
// Check if value is removed
let age = UserDefaults.standard.integer(forKey: "userAge")
print(age) // 0 (default value)
In this example, the userAge
key is removed, and retrieving it afterward returns the default Int
value, which is 0
.
Checking for Data Existence
Sometimes, you may want to check if a particular key exists in UserDefaults
. One way to do this is by using the object(forKey:)
method, which returns nil
if the key doesn’t exist.
Checking for a Value
if UserDefaults.standard.object(forKey: "userName") != nil {
print("User name is saved.")
} else {
print("User name is not saved.")
}
Using UserDefaults with Custom Types
UserDefaults
doesn’t directly support custom types, but you can use Codable
to encode and decode custom objects and store them as Data
.
Saving and Retrieving a Custom Type
Suppose we have a User
struct that we want to save in UserDefaults
.
struct User: Codable {
let name: String
let age: Int
}
let user = User(name: "Mahi", age: 30)
// Encode and save to UserDefaults
if let encoded = try? JSONEncoder().encode(user) {
UserDefaults.standard.set(encoded, forKey: "currentUser")
}
// Decode and retrieve from UserDefaults
if let savedUserData = UserDefaults.standard.data(forKey: "currentUser"),
let savedUser = try? JSONDecoder().decode(User.self, from: savedUserData) {
print(savedUser.name) // Mahi
}
In this example:
- We encode the
User
instance intoData
usingJSONEncoder
. - We save the encoded data in
UserDefaults
. - We then retrieve and decode the data back into a
User
instance usingJSONDecoder
.
Best Practices with UserDefaults
- Use Descriptive Keys: To avoid key collisions, use descriptive names, such as “userProfileName” instead of “name”.
- Avoid Storing Large Data:
UserDefaults
is intended for small data. For large data (like images or videos), useFileManager
. - Data Security: Do not store sensitive information, like passwords, in
UserDefaults
. Use Keychain for secure storage. - Avoid Overusing UserDefaults: Overuse of
UserDefaults
can lead to clutter and decreased performance. Use it primarily for user settings and preferences.
Using a Singleton UserDefaultsManager
To streamline managing UserDefaults
variables, we can create a singleton class called UserDefaultsManager
. This class will include properties for each UserDefaults
value, with custom getter and setter methods. This way, we have a single, consistent location for managing UserDefaults
.
Step 1: Define UserDefaultsManager
class UserDefaultsManager {
static let shared = UserDefaultsManager() // Singleton instance
private init() {} // Prevent external instantiation
var username: String? {
get {
UserDefaults.standard.string(forKey: "username")
}
set {
UserDefaults.standard.set(newValue, forKey: "username")
}
}
var isDarkModeEnabled: Bool {
get {
UserDefaults.standard.bool(forKey: "isDarkModeEnabled")
}
set {
UserDefaults.standard.set(newValue, forKey: "isDarkModeEnabled")
}
}
var isNotificationEnabled: Bool {
get {
UserDefaults.standard.bool(forKey: "isNotificationEnabled")
}
set {
UserDefaults.standard.set(newValue, forKey: "isNotificationEnabled")
}
}
}
In this class:
- We define each
UserDefaults
variable as a computed property with a custom getter and setter. - The
UserDefaultsManager
is a singleton, so only one instance exists, ensuring centralized access.
Step 2: Using UserDefaultsManager
With this setup, accessing or modifying UserDefaults values is simple and consistent:
// Set values
UserDefaultsManager.shared.username = "Mahi"
UserDefaultsManager.shared.isDarkModeEnabled = true
UserDefaultsManager.shared.isNotificationEnabled = false
// Get values
print(UserDefaultsManager.shared.username ?? "Guest") // Mahi
print(UserDefaultsManager.shared.isDarkModeEnabled) // true
print(UserDefaultsManager.shared.isNotificationEnabled) // false
Using a UserDefaultsManager
singleton allows for cleaner, more readable code. Each UserDefaults
value is encapsulated in its property, which reduces the risk of key mismanagement and improves code organization.
Summary of UserDefaults
Task | Code Example | Description |
---|---|---|
Save Data | UserDefaults.standard.set(value, forKey: "key") |
Saves a value with a specified key |
Retrieve Data | UserDefaults.standard.string(forKey: "key") |
Retrieves a value for the specified key |
Delete Data | UserDefaults.standard.removeObject(forKey: "key") |
Removes a value for the specified key |
Store Custom Types | Use JSONEncoder and JSONDecoder for encoding and decoding |
Allows saving custom types as Data |
Check for Key Existence | UserDefaults.standard.object(forKey: "key") != nil |
Checks if a key exists |
Conclusion
UserDefaults
is a convenient tool for saving user-specific settings and small amounts of persistent data in Swift apps. By using a UserDefaultsManager
singleton, you can further simplify and centralize access to UserDefaults
, making your code cleaner and less prone to errors.