Save Struct to UserDefaults
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
UserDefaults is meant for small preference-style values, not arbitrary Swift types. If you want to store a struct, the normal solution is to make it Codable, encode it into Data, save that data, and decode it when reading it back.
Why a Struct Cannot Be Stored Directly
UserDefaults only accepts property-list-compatible types directly, such as String, Int, Bool, Date, Data, arrays, and dictionaries made from those types. A custom Swift struct is not automatically one of those storage formats.
That means code like this is invalid:
The fix is not to force the struct into UserDefaults directly. The fix is to encode it into Data first.
Use Codable and Store Encoded Data
The most common approach uses Codable with JSONEncoder and JSONDecoder.
Reading the value back is the reverse operation:
This keeps the persistence format compact and makes the struct easy to evolve using standard Codable techniques.
Wrap Persistence in a Small Store Type
In production code, it is better to hide the raw key names and encoding logic behind a small helper. That keeps view controllers and SwiftUI views from duplicating persistence code.
That wrapper also gives you one place to handle migrations, fallback logic, and test injection later.
Choosing an Encoder and Planning for Changes
JSONEncoder is popular because its output is easy to inspect during debugging. PropertyListEncoder also works well and fits naturally with the property-list ecosystem. Either is fine as long as your read and write paths agree.
You should also think about versioning. If the struct changes later, older saved data may still exist on user devices. Default values, optional properties, or custom decoding logic can help keep backward compatibility manageable.
When UserDefaults Is the Wrong Tool
This pattern is appropriate for small settings-like objects such as the current profile, a selected filter, or a recent search preference. It is a poor fit for large datasets, rapidly changing collections, or sensitive credentials.
If the data is secret, use the Keychain. If the data is large or relational, use a database or files. Treat UserDefaults as a convenience store for lightweight app preferences, not as a general persistence layer.
Common Pitfalls
Trying to store the struct directly is the first mistake. Encode to Data first.
Using try? everywhere can hide data corruption or migration bugs. Surface failures where they matter.
Storing large models in UserDefaults leads to slow, messy persistence design. Keep it for small settings-oriented values.
Summary
- A Swift
structmust be encoded before it can be stored inUserDefaults. - '
Codableplus an encoder such asJSONEncoderis the standard approach.' - Save encoded
Data, then decode it when reading. - Wrap keys and encoding logic in a helper so persistence stays centralized.

