SkillAgentSearch skills...

SwiftyUserDefaults

Modern Swift API for NSUserDefaults

Install / Use

/learn @sunshinejr/SwiftyUserDefaults
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

SwiftyUserDefaults

Platforms CI Status CocoaPods compatible Carthage compatible SPM compatible Swift version Swift version Swift version Swift version

Modern Swift API for NSUserDefaults

SwiftyUserDefaults makes user defaults enjoyable to use by combining expressive Swifty API with the benefits of static typing. Define your keys in one place, use value types easily, and get extra safety and convenient compile-time checks for free.

Previous versions' documentation: Version 4.0.0, Version 3.0.1<br /> Migration guides: from 4.x to 5.x, from 4.0.0-alpha.1 to 4.0.0-alpha.3, from 3.x to 4.x

Version 5.0.0

<p align="center"> <a href="#features">Features</a> &bull; <a href="#usage">Usage</a> &bull; <a href="#codable">Codable</a> &bull; <a href="#nscoding">NSCoding</a> &bull; <a href="#rawrepresentable">RawRepresentable</a> &bull; <a href="#extending-existing-types">Extending existing types</a> &bull; <a href="#custom-types">Custom types</a> </p> <p align="center"> <a href="#property-wrappers">Property wrappers</a> &bull; <a href="#kvo">KVO</a> &bull; <a href="#keypath-dynamicMemberLookup">dynamicMemberLookup</a> &bull; <a href="#launch-arguments">Launch arguments</a> &bull; <a href="#utils">Utils</a> &bull; <a href="#installation">Installation</a> </p>

Features

There's only one step to start using SwiftyUserDefaults:

Define your keys!

extension DefaultsKeys {
    var username: DefaultsKey<String?> { .init("username") }
    var launchCount: DefaultsKey<Int> { .init("launchCount", defaultValue: 0) }
}

And just use it ;-)

// Get and set user defaults easily
let username = Defaults[\.username]
Defaults[\.hotkeyEnabled] = true

// Modify value types in place
Defaults[\.launchCount] += 1
Defaults[\.volume] -= 0.1
Defaults[\.strings] += "… can easily be extended!"

// Use and modify typed arrays
Defaults[\.libraries].append("SwiftyUserDefaults")
Defaults[\.libraries][0] += " 2.0"

// Easily work with custom serialized types
Defaults[\.color] = NSColor.white
Defaults[\.color]?.whiteComponent // => 1.0

If you use Swift 5.1 - good news! You can also use keyPath dynamicMemberLookup:

Defaults.color = NSColor.white

See more at the <a href="#keypath-dynamicMemberLookup">KeyPath dynamicMemberLookup</a> section.

Usage

Define your keys

To get the most out of SwiftyUserDefaults, define your user defaults keys ahead of time:

let colorKey = DefaultsKey<String>("color", defaultValue: "")

Just create a DefaultsKey object, put the type of the value you want to store in angle brackets, the key name in parentheses, and you're good to go. If you want to have a non-optional value, just provide a defaultValue in the key (look at the example above).

You can now use the Defaults shortcut to access those values:

Defaults[key: colorKey] = "red"
Defaults[key: colorKey] // => "red", typed as String

The compiler won't let you set a wrong value type, and fetching conveniently returns String.

Take shortcuts

For extra convenience, define your keys by extending magic DefaultsKeys class and adding static properties:

extension DefaultsKeys {
    var username: DefaultsKey<String?> { .init("username") }
    var launchCount: DefaultsKey<Int> { .init("launchCount", defaultValue: 0) }
}

And use the shortcut dot syntax:

Defaults[\.username] = "joe"
Defaults[\.launchCount] += 1

Supported types

SwiftyUserDefaults supports all of the standard NSUserDefaults types, like strings, numbers, booleans, arrays and dictionaries.

Here's a full table of built-in single value defaults:

| Single value | Array | | ---------------- | -------------------- | | String | [String] | | Int | [Int] | | Double | [Double] | | Bool | [Bool] | | Data | [Data] | | Date | [Date] | | URL | [URL] | | [String: Any] | [[String: Any]] |

But that's not all!

Codable

Since version 4, SwiftyUserDefaults support Codable! Just conform to DefaultsSerializable in your type:

final class FrogCodable: Codable, DefaultsSerializable {
    let name: String
 }

No implementation needed! By doing this you will get an option to specify an optional DefaultsKey:

let frog = DefaultsKey<FrogCodable?>("frog")

Additionally, you've got an array support for free:

let froggies = DefaultsKey<[FrogCodable]?>("froggies")

NSCoding

NSCoding was supported before version 4, but in this version we take the support on another level. No need for custom subscripts anymore! Support your custom NSCoding type the same way as with Codable support:

final class FrogSerializable: NSObject, NSCoding, DefaultsSerializable { ... }

No implementation needed as well! By doing this you will get an option to specify an optional DefaultsKey:

let frog = DefaultsKey<FrogSerializable?>("frog")

Additionally, you've got an array support also for free:

let froggies = DefaultsKey<[FrogSerializable]?>("froggies")

RawRepresentable

And the last but not least, RawRepresentable support! Again, the same situation like with NSCoding and Codable:

enum BestFroggiesEnum: String, DefaultsSerializable {
    case Andy
    case Dandy
}

No implementation needed as well! By doing this you will get an option to specify an optional DefaultsKey:

let frog = DefaultsKey<BestFroggiesEnum?>("frog")

Additionally, you've got an array support also for free:

let froggies = DefaultsKey<[BestFroggiesEnum]?>("froggies")

Extending existing types

Let's say you want to extend a support UIColor or any other type that is NSCoding, Codable or RawRepresentable. Extending it to be SwiftyUserDefaults-friendly should be as easy as:

extension UIColor: DefaultsSerializable {}

If it's not, we have two options:<br /> a) It's a custom type that we don't know how to serialize, in this case at Custom types<br /> b) It's a bug and it should be supported, in this case please file an issue (+ you can use custom types method as a workaround in the meantime)<br />

Custom types

If you want to add your own custom type that we don't support yet, we've got you covered. We use DefaultsBridges of many kinds to specify how you get/set values and arrays of values. When you look at DefaultsSerializable protocol, it expects two properties in each type: _defaults and _defaultsArray, where both are of type DefaultsBridge.

For instance, this is a bridge for single value data storing/retrieving using NSKeyedArchiver/NSKeyedUnarchiver:

public struct DefaultsKeyedArchiverBridge<T>: DefaultsBridge {

    public func get(key: String, userDefaults: UserDefaults) -> T? {
        userDefaults.data(forKey: key).flatMap(NSKeyedUnarchiver.unarchiveObject) as? T
    }

    public func save(key: String, value: T?, userDefaults: UserDefaults) {
        userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key)
    }

    public func deserialize(_ object: Any) -> T? {
        guard let data = object as? Data else { return nil }
        return NSKeyedUnarchiver.unarchiveObject(with: data) as? T
    }    
}

Bridge for default storing/retrieving array values:

public struct DefaultsArrayBridge<T: Collection>: DefaultsBridge {
    public func save(key: String, value: T?, userDefaults: UserDefaults) {
        userDefaults.set(value, forKey: key)
    }

    public func get(key: String, userDefaults: UserDefaults) -> T? {
        userDefaults.array(forKey: key) as? T
    }

    public func deserialize(_ object: Any) -> T? {
        nil
    }
}

Now, to use these bridges in our type we simply declare it as follows:

struct FrogCustomSerializable: DefaultsSerializable {

    static var _defaults: DefaultsKeyedArchiverBridge( { DefaultsKeyedArchiverBridge() }
    static var _defaultsArray: DefaultsKeyedArchiverBridge { DefaultsKeyedArchiverBridge() }

    let name: String
}

Unfortunately, if you find yourself in a situation where you need a custom bridge, you'll probably need to write your own:

final class DefaultsFrogBridge: DefaultsBridge {
    func get(key: String, userDefaults: UserDefaults) -> FrogCustomSerializable? {
        let name = userDefaults.string(forKey: key)
        return name.map(FrogCustomSerializable.init)
    }

    func save(key: String, value: FrogCustomSerializable?, userDefaults: UserDefaults) {
        userDefaults.set(value?.name, forKey: key)
    }

    func deserialize(_ object: Any) -> FrogCustomSerializable? {
        guard let nam
View on GitHub
GitHub Stars4.9k
CategoryDevelopment
Updated3d ago
Forks363

Languages

Swift

Security Score

100/100

Audited on Mar 26, 2026

No findings