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
Userobject with properties likename,age, andemail. - 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:
- Encode the
Userinstance toDatausingJSONEncoder. - Store the encoded
DatainUserDefaults.
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 encodeuserinto aDataobject. - We store the
Dataobject inUserDefaultswith a specified keycurrentUser.
Retrieving and Decoding the Object from UserDefaults
To retrieve the User object, we need to:
- Retrieve the
DatafromUserDefaultsusing the same key. - Decode the
Databack into aUserobject usingJSONDecoder.
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
Dataobject fromUserDefaults. - We use
JSONDecoder().decode(User.self, from: savedUserData)to decode the data back into a User instance. - We then access the properties of
savedUserto 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 toUserDefaults.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.,
currentUserorsettingsProfile). - Limit Object Size: Avoid storing large objects or complex structures in
UserDefaults. For more substantial data, consider usingFileManageror 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.