# sindresorhus/Defaults: a typed Swift facade over UserDefaults

> Defaults wraps UserDefaults in strongly typed keys with declared defaults, adds a SwiftUI property wrapper and Codable storage, and targets macOS 11+, iOS 14+, tvOS 14+, watchOS 9+ and visionOS 1+. It is a convenience layer, not a replacement for the underlying store.

**sindresorhus/Defaults** — 💾 Swifty and modern UserDefaults

- Repository: https://github.com/sindresorhus/Defaults
- Website: https://swiftpackageindex.com/sindresorhus/Defaults/documentation/defaults
- Stars: 2,502 · Forks: 163
- Language: Swift
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/sindresorhus-defaults

## The problem: untyped keys and duplicated default values

UserDefaults stores key-value pairs persistently across launches, but it deals in strings and Any. Nothing stops two parts of an app from disagreeing about whether the key is "quality" or "Quality", and nothing records what the default value was supposed to be. The README frames the benefit directly: you define strongly-typed identifiers in a single place and can use them everywhere, and you also define the default values in a single place instead of having to remember what default value you used in other places. That is the whole pitch. It is aimed at app developers on Apple platforms who already use UserDefaults and want the compiler to catch a mistyped key or a value of the wrong type. The README states the library is used in production by all of the author's apps, which he lists on his site, and gives 4 million+ users as the figure; that is a claim from the README, not an independent measurement.

## How Defaults works: a typed facade over the same store

The README is explicit that Defaults uses UserDefaults underneath but exposes a type-safe facade. So the data still lands in the standard preferences store; what changes is the API in front of it. You declare a key as a static property on Defaults.Keys, giving the key name, the type and the default value in one expression. Reads and writes go through a subscript on Defaults, and the library returns the declared default when no value has been written. Because the type is part of the key declaration, a Key<Double> cannot be read as a String without a compile error. The README also states that data is stored as JSON-serialized values, which is what makes the store debuggable, and that Codable and NSSecureCoding values are supported, so enums and custom types can be persisted. Observation is listed as a highlight, meaning you can subscribe to changes on a key rather than polling. iCloud support is listed too, described as automatic synchronisation of data between devices. The README names these capabilities but does not document their mechanics; the full documentation lives on the Swift Package Index, linked from the repository.

## Installing Defaults with Swift Package Manager

The README gives one installation route: add the repository URL in the Swift Package Manager tab in Xcode. The repository also contains a Package.swift, so it is a normal Swift package and can be added as a dependency in a manifest as well, though the README itself only describes the Xcode flow. Adding it in Xcode means opening your project, choosing File > Add Package Dependencies, and pasting the URL. Xcode resolves the package and lets you pick the product to link against your target.

```bash
https://github.com/sindresorhus/Defaults
```

After the package resolves, import it and declare a key. The README's own example defines a double-backed key named "quality" with a default of 0.8, then reads and writes through the subscript. The comment markers in the README show the expected values: the first read returns 0.8 because nothing has been written yet, and after assigning 0.5 the subscript returns 0.5.

```swift
import Defaults

extension Defaults.Keys {
	static let quality = Key<Double>("quality", default: 0.8)
}

Defaults[.quality]
//=> 0.8

Defaults[.quality] = 0.5
//=> 0.5
```

In SwiftUI, the README shows the @Default property wrapper binding the same key to a view. The example puts a Slider on the quality value with a range of 0 to 1, and because the wrapper is a binding, dragging the slider writes through to the store and the view updates when the value changes.

```swift
struct ContentView: View {
	@Default(.quality) var quality

	var body: some View {
		Slider(value: $quality, in: 0...1)
	}
}
```

The README also mentions a convenience SwiftUI Toggle component, but does not show its API, so check the full documentation before wiring one up.

## Where Defaults is the wrong tool

Defaults is a facade, and the README says so. Everything it stores still goes through UserDefaults, which is designed for preferences, not for application data. If you are persisting a list of records, a cache, or anything you need to query, filter, or migrate between schema versions, this is the wrong layer. The README does not document a migration system, a schema version, or a rollback path, so versioning your stored shape is work you own. There is also a platform floor to check: macOS 11+, iOS 14+, tvOS 14+, watchOS 9+ and visionOS 1+. A project still supporting iOS 13 cannot use it. The JSON serialization that makes values debuggable also means a value written by one version of your key declaration may not decode cleanly if you later change the type of that key, and the README gives no guidance on that case. Finally, if you only need one or two settings inside a SwiftUI view and do not care about observing them outside SwiftUI, @AppStorage is built in and requires no dependency; the README's own comparison lists the reasons Defaults goes further, and if none of those reasons apply to you, the extra package is not earning its place.

## Defaults compared with @AppStorage

The README devotes a section to this comparison, which is the most useful part of it for a decision. @AppStorage is Apple's SwiftUI property wrapper. Its limits, as the README describes them, are that identifiers and default values get scattered across views, it only works inside SwiftUI, it cannot observe value updates, it supports fewer types, and it has no path to custom types. Defaults answers each of those: keys and defaults live in one place, the API works outside SwiftUI, changes can be observed, Codable and NSSecureCoding types are supported, and custom serialization is possible. The trade-off is a third-party dependency and a minimum platform floor, against a first-party wrapper with no install step. If your app is SwiftUI-only, stores a handful of primitive settings, and never needs to read them from a background service or a model layer, @AppStorage is the smaller answer. The moment a non-view type needs the same value, or a stored enum needs to round-trip, Defaults is doing work that @AppStorage will not.

## Maintenance, licence and upgrade cost

The repository is not archived. The most recent push recorded is 2026-06-23, and release 9.0.9 is dated the same day, so the project has shipped within the last several months rather than sitting idle. The release history shows a 9.0.8 in March 2026 and a 9.0.7 three days before it, which suggests small patch releases rather than a slow trickle, though the README does not describe what any of those releases changed. There are two maintainers listed: Sindre Sorhus and @hank121314. The licence is MIT, which is permissive and places few obligations on how you redistribute the package, but this is a description of the licence file present in the repository, not legal advice; read the licence text and your own organisation's policy before shipping. The upgrade cost is the usual Swift Package Manager one: bump the version constraint, rebuild, and check that nothing in your key declarations relied on behaviour that changed. Because the README documents no deprecation policy or migration guide, treat a major version bump as something to test rather than assume.

## Conclusion

Adopt Defaults if your app already stores small settings in UserDefaults and you want those keys declared once, typed, and observable in SwiftUI, with Codable values supported. Do not adopt it for large datasets, relational queries, or anything that needs a migration story beyond what you write yourself; it is a facade over the same plist-backed store, with the same size and synchronisation characteristics. Before committing, verify the minimum deployment targets against your project, confirm that your custom types are Codable or NSSecureCoding, and read the full documentation on the Swift Package Index to see the observation and iCloud APIs, which the README only names.

## FAQ

### What is Defaults and what does it do?

Defaults is a Swift package that wraps UserDefaults in a type-safe facade. You declare a key with its type and default value once, then read and write it through a subscript, and it can also be used as a SwiftUI property wrapper.

### How do I install Defaults?

The README gives one route: add https://github.com/sindresorhus/Defaults in the Swift Package Manager tab in Xcode. The repository also contains a Package.swift, so it is a standard Swift package.

### Which platforms does Defaults support?

The README lists macOS 11+, iOS 14+, tvOS 14+, watchOS 9+ and visionOS 1+.

### Does Defaults replace UserDefaults?

No. The README states it uses UserDefaults underneath but exposes a type-safe facade, so values are still stored in the same preferences store.

### Can Defaults store custom types like enums?

Yes. The README lists Codable support, so any Codable value such as an enum can be stored, and NSSecureCoding values are supported as well. It also says you can serialize and deserialize your own type in your own way.

## Sources

- [License: MIT](https://github.com/sindresorhus/Defaults/blob/main/LICENSE)
- [Project website](https://swiftpackageindex.com/sindresorhus/Defaults/documentation/defaults)
- [README](https://github.com/sindresorhus/Defaults/blob/main/README.md)
- [Releases](https://github.com/sindresorhus/Defaults/releases)
- [sindresorhus/Defaults on GitHub](https://github.com/sindresorhus/Defaults)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sindresorhus-defaults
