Logo for tanaschita.com

Storing Swift structs in UserDefaults

Why Codable works but often isn't the best choice.

05 Jan 2026 · 4 min read

UserDefaults is often the first place we reach for when we want to persist small pieces of data. It's simple, built into the system, and works well for basic types like strings, numbers, and booleans.

But what if we try to store a Swift struct in UserDefaults. Let's look at how to do that.

Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE

What UserDefaults can store by default

Out of the box, UserDefaults only supports data types which are also supported by property lists. Those are:

We can also store arrays and dictionaries but only if their elements are one of the types listed above.

Storing a struct in UserDefaults

When we try to store a Swift struct in UserDefaults, the compiler does not complain:

struct Content {
let title: String
}
let content = Content(title: "Some title")
let userDefaults = UserDefaults.standard
userDefaults.set(content, forKey: "content")

However, once we run the app, it crashes with the following exception:

Exception: Attempt to insert non-property list object for key content

To store a Swift struct in UserDefaults, we need to convert it to one of the supported types. One common approach is to use Codable and encode the struct into Data:

struct Content: Codable {
let title: String
}
if let contentData = try? JSONEncoder().encode(content) {
userDefaults.set(contentData, forKey: "content")
}

To read the stored data, we can use a JSON decoder:

if let contentData = userDefaults.data(forKey: "content"),
let content = try? JSONDecoder().decode(Content.self, from: contentData) {
print(content)
}

This pattern is easy to implement. But it comes with an important caveat.

Why the Codable approach is error-prone

The biggest issue with this approach appears when our model changes.

If we later modify Content, for example by renaming a property, changing its type or by adding another property, the JSON decoder will not be able to decode a previously saved object.

Unlike a database-based storage, there's no built-in migration mechanism. Since we don't get any compiler hints or errors when changing the struct, this kind of storage may easily lead to bugs or lost data.

An alternative: storing properties individually

If we want more control and resilience to future changes, we can store individual properties instead of encoding the entire struct:

func writeContent(_ content: Content) {
userDefaults.set(content.title, forKey: "contentTitle")
userDefaults.set(content.amount, forKey: "contentAmount")
}
func readContent() -> Content {
let title = userDefaults.string(forKey: "contentTitle") ?? ""
let amount = (userDefaults.object(forKey:) as? Int) ?? 0
return Content(title: title, amount: amount)
}

With this approach, we gain flexibility:

  • We can add or remove properties without breaking decoding
  • We can provide sensible default values
  • Renaming properties becomes much safer

The trade-off is a bit more manual work, but in return, we get more predictable behavior as our app evolves.

Sponsorship logo
Preparing for a technical iOS job interview
Preparing for a technical iOS Job Interview with over 300 questions & answers. Covering Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, iOS File System, Core Data, Concurrency with async/await, Security, Automated Testing, Dependency Management, AI & Machine Learning and more.
LEARN MORE
Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE
Sponsorship logo
Become a sponsor of tanaschita.com
By publishing an article on different iOS topics every week, tanaschita.com is constantly growing in the developer community and may provide a great audience for you as a sponsor.
CLICK TO LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

file system

persistence

swift

ios

Working with files and directories in iOS

Understanding the app sandbox and how to access system directories.

06 Apr 2026 · 6 min read

Latest articles and tips

© 2026 tanaschita.com

Privacy policy

Impressum