Blog / October 27, 2024 / 5 mins read / By Mahi Garg

UserDefaults in Swift

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, and String to UserDefaults.
  • 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 into Data using JSONEncoder.
  • We save the encoded data in UserDefaults.
  • We then retrieve and decode the data back into a User instance using JSONDecoder.

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), use FileManager.
  • 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.

Comments