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 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
User
instance toData
usingJSONEncoder
. - Store the encoded
Data
inUserDefaults
.
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 encodeuser
into aData
object. - We store the
Data
object inUserDefaults
with a specified keycurrentUser
.
Retrieving and Decoding the Object from UserDefaults
To retrieve the User
object, we need to:
- Retrieve the
Data
fromUserDefaults
using the same key. - Decode the
Data
back into aUser
object 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
Data
object fromUserDefaults
. - 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 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.,
currentUser
orsettingsProfile
). - Limit Object Size: Avoid storing large objects or complex structures in
UserDefaults
. For more substantial data, consider usingFileManager
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.