NSUserDefaults
iOS Development
Swift
Property List
Debugging

Attempt to set a non-property-list object as an NSUserDefaults

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

This error means you tried to store a value in UserDefaults that is not one of the supported property-list types. UserDefaults is meant for small preference-like values, not arbitrary Swift objects or rich model graphs.

What UserDefaults Can Store Directly

Directly supported values include:

  • 'String'
  • 'Int, Double, Bool, and other scalar wrappers'
  • 'Date'
  • 'Data'
  • arrays containing supported values
  • dictionaries containing supported values

A custom struct or class is not supported automatically.

This fails:

swift
1import Foundation
2
3struct Settings {
4    let theme: String
5    let notificationsEnabled: Bool
6}
7
8let settings = Settings(theme: "dark", notificationsEnabled: true)
9UserDefaults.standard.set(settings, forKey: "settings")

The error appears because Settings is not a property-list type.

Encode Custom Types First

The normal modern fix is to make the type Codable and store the encoded data:

swift
1import Foundation
2
3struct Settings: Codable {
4    let theme: String
5    let notificationsEnabled: Bool
6}
7
8let defaults = UserDefaults.standard
9let settings = Settings(theme: "dark", notificationsEnabled: true)
10
11let encoded = try JSONEncoder().encode(settings)
12defaults.set(encoded, forKey: "settings")

Reading the value back requires decoding:

swift
1if let data = defaults.data(forKey: "settings") {
2    let restored = try JSONDecoder().decode(Settings.self, from: data)
3    print(restored.theme)
4}

This works because Data is a supported type even though the original object is not.

A Dictionary Works Only for Simple Values

If the data is tiny and flat, you can sometimes store a dictionary:

swift
1let settings: [String: Any] = [
2    "theme": "dark",
3    "notificationsEnabled": true
4]
5
6UserDefaults.standard.set(settings, forKey: "settings")

This is acceptable only when every nested value is also property-list compatible. It becomes fragile quickly because everything is string-keyed and type safety is weak.

UserDefaults Is Not a General Database

A common design mistake is using UserDefaults as lightweight object persistence for everything. It is a better fit for:

  • theme settings
  • selected options
  • feature flags
  • small user preferences

It is a poor fit for:

  • large object graphs
  • image blobs
  • caches
  • queryable data
  • anything needing schema migration beyond simple preferences

If the value is large or structurally rich, use files, Core Data, SQLite, or another persistence layer.

JSONEncoder vs PropertyListEncoder

Both are reasonable because the final stored value is Data. JSONEncoder is often convenient because the bytes are easier to inspect during debugging. PropertyListEncoder is also fine if you want an Apple-style plist representation.

Example with PropertyListEncoder:

swift
1import Foundation
2
3struct Settings: Codable {
4    let theme: String
5    let notificationsEnabled: Bool
6}
7
8let settings = Settings(theme: "light", notificationsEnabled: false)
9let data = try PropertyListEncoder().encode(settings)
10UserDefaults.standard.set(data, forKey: "settings")

The important rule is not the encoder choice. It is that the value you write into UserDefaults must be a supported type.

Old Name, Same Rules

The older Objective-C error text mentions NSUserDefaults, while modern Swift usually uses UserDefaults. The storage rules are the same. The class naming style changed, but the requirement for property-list compatibility did not.

Common Pitfalls

  • Trying to store a custom struct or class directly instead of encoding it first.
  • Building a dictionary that looks valid at the top level but contains unsupported nested objects.
  • Treating UserDefaults as a general persistence layer for large or complex data.
  • Forgetting to handle decode failures when the stored format changes over time.
  • Using [String: Any] everywhere and losing type safety when a Codable model would be clearer.

Summary

  • 'UserDefaults stores only property-list compatible values directly.'
  • Custom objects must be encoded into a supported type such as Data.
  • 'Codable with JSONEncoder or PropertyListEncoder is the usual modern fix.'
  • Small dictionaries are fine only when every nested value is also supported.
  • Use UserDefaults for lightweight preferences, not for general-purpose object storage.

Course illustration
Course illustration

All Rights Reserved.