Blog / November 11, 2024 / 4 mins read / By Mahi Garg

Custom Object in UserDefaults in Swift

In Swift, UserDefaults is a convenient class for storing small, persistent data like user preferences and app settings. While UserDefaults is typically used for basic data types such as String, Int, Bool, Array, and Dictionary, it doesn’t directly support storing custom objects. However, with Swift’s Codable protocol, we can easily save and retrieve custom objects in UserDefaults. In this blog, we’ll explore how to do just that, step-by-step, and look at best practices along the way.

Why Save Custom Objects in UserDefaults?

While it’s generally best to use UserDefaults for simple data, there are cases where you might want to persist custom objects. For instance:

  • User Profiles – If you have a custom User object with properties like name, age, and email.
  • App Configurations – An object with multiple properties for app settings.
  • Session Data – To store specific session information across app launches.

For more complex or sensitive data (like images, sensitive tokens, or large datasets), consider using Keychain or FileManager.

Creating a Custom Codable Object

Let’s create a User struct as our custom object to be saved in UserDefaults. We’ll make User conform to Codable (a combination of Encodable and Decodable protocols) so that we can easily encode and decode it.

struct User: Codable {
    let name: String
    let age: Int
    let email: String
}

By making User conform to Codable, we allow it to be easily transformed into Data for storage and back into a User object for retrieval.

Encoding and Saving the Object in UserDefaults

To store the User object in UserDefaults, we need to:

  1. Encode the User instance to Data using JSONEncoder.
  2. Store the encoded Data in UserDefaults.

Example: Encoding and Saving

let user = User(name: "Mahi", age: 25, email: "mahi@example.com")

// Encode and save to UserDefaults
if let encodedUser = try? JSONEncoder().encode(user) {
    UserDefaults.standard.set(encodedUser, forKey: "currentUser")
}

Here:

  • We create an instance of User.
  • We use JSONEncoder().encode(user) to encode user into a Data object.
  • We store the Data object in UserDefaults with a specified key currentUser.

Retrieving and Decoding the Object from UserDefaults

To retrieve the User object, we need to:

  • Retrieve the Data from UserDefaults using the same key.
  • Decode the Data back into a User object using JSONDecoder.

Example: Retrieving and Decoding

if let savedUserData = UserDefaults.standard.data(forKey: "currentUser"),
   let savedUser = try? JSONDecoder().decode(User.self, from: savedUserData) {
    print("Name: \(savedUser.name), Age: \(savedUser.age), Email: \(savedUser.email)")
}

In this example:

  • We retrieve the Data object from UserDefaults.
  • We use JSONDecoder().decode(User.self, from: savedUserData) to decode the data back into a User instance.
  • We then access the properties of savedUser to verify the data.

Managing Optional Custom Objects in UserDefaults

It’s often useful to make sure that retrieving an object from UserDefaults doesn’t result in unexpected errors. By creating an extension on UserDefaults, we can manage optional values more safely.

extension UserDefaults {
    func setObject<T: Encodable>(_ object: T, forKey key: String) {
        if let encoded = try? JSONEncoder().encode(object) {
            set(encoded, forKey: key)
        }
    }
    
    func getObject<T: Decodable>(forKey key: String, as type: T.Type) -> T? {
        if let data = data(forKey: key),
           let decodedObject = try? JSONDecoder().decode(type, from: data) {
            return decodedObject
        }
        return nil
    }
}

With these methods:

  • setObject(_:forKey:) encodes an object and saves it to UserDefaults.
  • getObject(forKey:as:) retrieves and decodes an object of the specified type.

Updating and Deleting Custom Objects in UserDefaults

If you need to update or delete a custom object in UserDefaults, you can simply overwrite the value or remove it.

Updating a Custom Object

Updating is as simple as saving a new instance of the object with the same key.

let updatedUser = User(name: "Mahi", age: 26, email: "mahi.new@example.com")
UserDefaults.standard.setObject(updatedUser, forKey: "currentUser")
Deleting a Custom Object

To delete the object, use removeObject(forKey:).

UserDefaults.standard.removeObject(forKey: "currentUser")

Best Practices for Storing Custom Objects in UserDefaults

  • Use Descriptive Keys: When storing custom objects, choose descriptive keys to avoid key collisions (e.g., currentUser or settingsProfile).
  • Limit Object Size: Avoid storing large objects or complex structures in UserDefaults. For more substantial data, consider using FileManager or a database.
  • Encapsulate Storage Logic: Keep encoding and decoding logic contained within a helper class, such as a UserDefaultsManager, for better organization and code reuse.

Conclusion

Storing custom objects in UserDefaults is straightforward with Swift’s Codable protocol. By encoding objects as Data and decoding them upon retrieval, you can extend UserDefaults to handle more complex data types. Just remember to keep data size manageable, use descriptive keys, and consider more advanced storage solutions for larger datasets.

Comments