[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-6850":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":19,"hasPages":19,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":16,"starSnapshotCount":16,"syncStatus":29,"lastSyncTime":30,"discoverSource":31},6850,"SwiftyUserDefaults","sunshinejr\u002FSwiftyUserDefaults","sunshinejr","Modern Swift API for NSUserDefaults","http:\u002F\u002Fradex.io\u002Fswift\u002Fnsuserdefaults\u002Fstatic",null,"Swift",4893,362,49,48,0,59.68,"MIT License",false,"master",[22,23,24,25],"ios","nsuserdefaults","swift","swifty","2026-06-12 04:00:30","# SwiftyUserDefaults\n\n![Platforms](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fplatforms-ios%20%7C%20osx%20%7C%20watchos%20%7C%20tvos-lightgrey.svg)\n[![CI Status](https:\u002F\u002Fapi.travis-ci.org\u002Fsunshinejr\u002FSwiftyUserDefaults.svg?branch=master)](https:\u002F\u002Ftravis-ci.org\u002Fsunshinejr\u002FSwiftyUserDefaults)\n[![CocoaPods compatible](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FCocoaPods-compatible-4BC51D.svg?style=flat)](#cocoapods)\n[![Carthage compatible](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FCarthage-compatible-4BC51D.svg?style=flat)](#carthage)\n[![SPM compatible](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FSPM-compatible-4BC51D.svg?style=flat)](#swift-package-manager)\n![Swift version](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fswift-4.1-orange.svg)\n![Swift version](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fswift-4.2-orange.svg)\n![Swift version](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fswift-5.0-orange.svg)\n![Swift version](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fswift-5.1-orange.svg)\n\n#### Modern Swift API for `NSUserDefaults`\n###### 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.\n\nPrevious versions' documentation: [Version 4.0.0](https:\u002F\u002Fgithub.com\u002Fsunshinejr\u002FSwiftyUserDefaults\u002Fblob\u002F566ace16ee91242b61e2e9da6cdbe7dfdadd926c\u002FREADME.md), [Version 3.0.1](https:\u002F\u002Fgithub.com\u002Fsunshinejr\u002FSwiftyUserDefaults\u002Fblob\u002F14b629b035bf6355b46ece22c3851068a488a895\u002FREADME.md)\u003Cbr \u002F>\nMigration guides: [from 4.x to 5.x](MigrationGuides\u002Fmigration_4_to_5.md), [from 4.0.0-alpha.1 to 4.0.0-alpha.3](MigrationGuides\u002Fmigration_4_alpha_1_to_4_alpha_2.md), [from 3.x to 4.x](MigrationGuides\u002Fmigration_3_to_4.md)\n\n# Version 5.0.0\n\n\u003Cp align=\"center\">\n    \u003Ca href=\"#features\">Features\u003C\u002Fa> &bull;\n    \u003Ca href=\"#usage\">Usage\u003C\u002Fa> &bull;\n    \u003Ca href=\"#codable\">Codable\u003C\u002Fa> &bull;\n    \u003Ca href=\"#nscoding\">NSCoding\u003C\u002Fa> &bull;\n    \u003Ca href=\"#rawrepresentable\">RawRepresentable\u003C\u002Fa> &bull;\n    \u003Ca href=\"#extending-existing-types\">Extending existing types\u003C\u002Fa> &bull;\n    \u003Ca href=\"#custom-types\">Custom types\u003C\u002Fa>\n\u003C\u002Fp>\n\u003Cp align=\"center\">\n    \u003Ca href=\"#property-wrappers\">Property wrappers\u003C\u002Fa> &bull;\n    \u003Ca href=\"#kvo\">KVO\u003C\u002Fa> &bull;\n    \u003Ca href=\"#keypath-dynamicMemberLookup\">dynamicMemberLookup\u003C\u002Fa> &bull;\n    \u003Ca href=\"#launch-arguments\">Launch arguments\u003C\u002Fa> &bull;\n    \u003Ca href=\"#utils\">Utils\u003C\u002Fa> &bull;\n    \u003Ca href=\"#installation\">Installation\u003C\u002Fa>\n\u003C\u002Fp>\n\n## Features\n\n**There's only one step to start using SwiftyUserDefaults:**\n\nDefine your keys!\n\n```swift\nextension DefaultsKeys {\n    var username: DefaultsKey\u003CString?> { .init(\"username\") }\n    var launchCount: DefaultsKey\u003CInt> { .init(\"launchCount\", defaultValue: 0) }\n}\n```\n\nAnd just use it ;-)\n\n```swift\n\u002F\u002F Get and set user defaults easily\nlet username = Defaults[\\.username]\nDefaults[\\.hotkeyEnabled] = true\n\n\u002F\u002F Modify value types in place\nDefaults[\\.launchCount] += 1\nDefaults[\\.volume] -= 0.1\nDefaults[\\.strings] += \"… can easily be extended!\"\n\n\u002F\u002F Use and modify typed arrays\nDefaults[\\.libraries].append(\"SwiftyUserDefaults\")\nDefaults[\\.libraries][0] += \" 2.0\"\n\n\u002F\u002F Easily work with custom serialized types\nDefaults[\\.color] = NSColor.white\nDefaults[\\.color]?.whiteComponent \u002F\u002F => 1.0\n```\n\nIf you use Swift 5.1 - good news! You can also use keyPath `dynamicMemberLookup`:\n```swift\nDefaults.color = NSColor.white\n```\n\nSee more at the \u003Ca href=\"#keypath-dynamicMemberLookup\">KeyPath dynamicMemberLookup\u003C\u002Fa> section.\n\n## Usage\n\n### Define your keys\n\nTo get the most out of SwiftyUserDefaults, define your user defaults keys ahead of time:\n\n```swift\nlet colorKey = DefaultsKey\u003CString>(\"color\", defaultValue: \"\")\n```\n\nJust 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).\n\nYou can now use the `Defaults` shortcut to access those values:\n\n```swift\nDefaults[key: colorKey] = \"red\"\nDefaults[key: colorKey] \u002F\u002F => \"red\", typed as String\n```\n\nThe compiler won't let you set a wrong value type, and fetching conveniently returns `String`.\n\n### Take shortcuts\n\nFor extra convenience, define your keys by extending magic `DefaultsKeys` class and adding static properties:\n\n```swift\nextension DefaultsKeys {\n    var username: DefaultsKey\u003CString?> { .init(\"username\") }\n    var launchCount: DefaultsKey\u003CInt> { .init(\"launchCount\", defaultValue: 0) }\n}\n```\n\nAnd use the shortcut dot syntax:\n\n```swift\nDefaults[\\.username] = \"joe\"\nDefaults[\\.launchCount] += 1\n```\n\n### Supported types\n\nSwiftyUserDefaults supports all of the standard `NSUserDefaults` types, like strings, numbers, booleans, arrays and dictionaries.\n\nHere's a full table of built-in single value defaults:\n\n| Single value     | Array                |\n| ---------------- | -------------------- |\n| `String`         | `[String]`           |\n| `Int`            | `[Int]`              |\n| `Double`         | `[Double]`           |\n| `Bool`           | `[Bool]`             |\n| `Data`           | `[Data]`             |\n| `Date`           | `[Date]`             |\n| `URL`            | `[URL]`              |\n| `[String: Any]`  | `[[String: Any]]`    |\n\nBut that's not all!\n\n## Codable\n\nSince version 4, `SwiftyUserDefaults` support `Codable`! Just conform to `DefaultsSerializable` in your type:\n```swift\nfinal class FrogCodable: Codable, DefaultsSerializable {\n    let name: String\n }\n```\n\nNo implementation needed! By doing this you will get an option to specify an optional `DefaultsKey`:\n```swift\nlet frog = DefaultsKey\u003CFrogCodable?>(\"frog\")\n```\n\nAdditionally, you've got an array support for free:\n```swift\nlet froggies = DefaultsKey\u003C[FrogCodable]?>(\"froggies\")\n```\n\n## NSCoding\n\n`NSCoding` was supported before version 4, but in this version we take the support on another level. No need for custom subscripts anymore!\nSupport your custom `NSCoding` type the same way as with `Codable` support:\n```\nfinal class FrogSerializable: NSObject, NSCoding, DefaultsSerializable { ... }\n```\n\nNo implementation needed as well! By doing this you will get an option to specify an optional `DefaultsKey`:\n```swift\nlet frog = DefaultsKey\u003CFrogSerializable?>(\"frog\")\n```\n\nAdditionally, you've got an array support also for free:\n```swift\nlet froggies = DefaultsKey\u003C[FrogSerializable]?>(\"froggies\")\n```\n\n## RawRepresentable\n\nAnd the last but not least, `RawRepresentable` support! Again, the same situation like with `NSCoding` and `Codable`:\n```swift\nenum BestFroggiesEnum: String, DefaultsSerializable {\n    case Andy\n    case Dandy\n}\n```\n\nNo implementation needed as well! By doing this you will get an option to specify an optional `DefaultsKey`:\n```swift\nlet frog = DefaultsKey\u003CBestFroggiesEnum?>(\"frog\")\n```\n\nAdditionally, you've got an array support also for free:\n```swift\nlet froggies = DefaultsKey\u003C[BestFroggiesEnum]?>(\"froggies\")\n```\n\n## Extending existing types\n\nLet's say you want to extend a support `UIColor` or any other type that is `NSCoding`, `Codable` or `RawRepresentable`.\nExtending it to be `SwiftyUserDefaults`-friendly should be as easy as:\n```swift\nextension UIColor: DefaultsSerializable {}\n```\n\nIf it's not, we have two options:\u003Cbr \u002F>\na) It's a custom type that we don't know how to serialize, in this case at [Custom types](#custom-types)\u003Cbr \u002F>\nb) It's a bug and it should be supported, in this case please file an issue (+ you can use [custom types](#custom-types) method as a workaround in the meantime)\u003Cbr \u002F>\n\n## Custom types\n\nIf you want to add your own custom type that we don't support yet, we've got you covered. We use `DefaultsBridge`s of many kinds to specify how you get\u002Fset 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`.\n\nFor instance, this is a bridge for single value data storing\u002Fretrieving using `NSKeyedArchiver`\u002F`NSKeyedUnarchiver`:\n```swift\npublic struct DefaultsKeyedArchiverBridge\u003CT>: DefaultsBridge {\n\n    public func get(key: String, userDefaults: UserDefaults) -> T? {\n        userDefaults.data(forKey: key).flatMap(NSKeyedUnarchiver.unarchiveObject) as? T\n    }\n\n    public func save(key: String, value: T?, userDefaults: UserDefaults) {\n        userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key)\n    }\n\n    public func deserialize(_ object: Any) -> T? {\n        guard let data = object as? Data else { return nil }\n        return NSKeyedUnarchiver.unarchiveObject(with: data) as? T\n    }    \n}\n```\n\nBridge for default storing\u002Fretrieving array values:\n```swift\npublic struct DefaultsArrayBridge\u003CT: Collection>: DefaultsBridge {\n    public func save(key: String, value: T?, userDefaults: UserDefaults) {\n        userDefaults.set(value, forKey: key)\n    }\n\n    public func get(key: String, userDefaults: UserDefaults) -> T? {\n        userDefaults.array(forKey: key) as? T\n    }\n\n    public func deserialize(_ object: Any) -> T? {\n        nil\n    }\n}\n```\n\nNow, to use these bridges in our type we simply declare it as follows:\n```swift\nstruct FrogCustomSerializable: DefaultsSerializable {\n\n    static var _defaults: DefaultsKeyedArchiverBridge( { DefaultsKeyedArchiverBridge() }\n    static var _defaultsArray: DefaultsKeyedArchiverBridge { DefaultsKeyedArchiverBridge() }\n\n    let name: String\n}\n```\n\nUnfortunately, if you find yourself in a situation where you need a custom bridge, you'll probably need to write your own:\n```swift\nfinal class DefaultsFrogBridge: DefaultsBridge {\n    func get(key: String, userDefaults: UserDefaults) -> FrogCustomSerializable? {\n        let name = userDefaults.string(forKey: key)\n        return name.map(FrogCustomSerializable.init)\n    }\n\n    func save(key: String, value: FrogCustomSerializable?, userDefaults: UserDefaults) {\n        userDefaults.set(value?.name, forKey: key)\n    }\n\n    func deserialize(_ object: Any) -> FrogCustomSerializable? {\n        guard let name = object as? String else { return nil }\n\n        return FrogCustomSerializable(name: name)\n    }\n}\n\nfinal class DefaultsFrogArrayBridge: DefaultsBridge {\n    func get(key: String, userDefaults: UserDefaults) -> [FrogCustomSerializable]? {\n        userDefaults.array(forKey: key)?\n            .compactMap { $0 as? String }\n            .map(FrogCustomSerializable.init)\n    }\n\n    func save(key: String, value: [FrogCustomSerializable]?, userDefaults: UserDefaults) {\n        let values = value?.map { $0.name }\n        userDefaults.set(values, forKey: key)\n    }\n\n    func deserialize(_ object: Any) -> [FrogCustomSerializable]? {\n        guard let names = object as? [String] else { return nil }\n\n        return names.map(FrogCustomSerializable.init)\n    }\n}\n\nstruct FrogCustomSerializable: DefaultsSerializable, Equatable {\n\n    static var _defaults: DefaultsFrogBridge { DefaultsFrogBridge() }\n    static var _defaultsArray: DefaultsFrogArrayBridge { DefaultsFrogArrayBridge() }\n\n    let name: String\n}\n```\n\nTo support existing types with different bridges, you can extend it similarly:\n```swift\nextension Data: DefaultsSerializable {\n    public static var _defaultsArray: DefaultsArrayBridge\u003C[T]> { DefaultsArrayBridge() }\n    public static var _defaults: DefaultsDataBridge { DefaultsDataBridge() }\n}\n```\n\nAlso, take a look at our source code (or tests) to see more examples of bridges. If you find yourself confused with all these bridges, please [create an issue](https:\u002F\u002Fgithub.com\u002Fsunshinejr\u002FSwiftyUserDefaults\u002Fissues\u002Fnew) and we will figure something out.\n\n## Property wrappers\n\nSwiftyUserDefaults provides property wrappers for Swift 5.1! The property wrapper, `@SwiftyUserDefault`, provides an option to use it with key path and options: caching or observing.\n\n*Caching* means that we will store the value for you and do not hit the `UserDefaults` for value almost never, only for the first value fetch.\n\n*Observing* means we will observe, via KVO, your property so you don't have to worry if it was saved somewhere else and you use caching.\n\nNow usage! Given keys:\n```swift\nextension DefaultsKeys {\n    var userColorScheme: DefaultsKey\u003CString> { .init(\"userColorScheme\", defaultValue: \"default\") }\n    var userThemeName: DefaultsKey\u003CString?> { .init(\"userThemeName\") }\n    var userLastLoginDate: DefaultsKey\u003CDate?> { .init(\"userLastLoginDate\") }\n}\n```\n\nYou can declare a `Settings` struct:\n```swift\nstruct Settings {\n    @SwiftyUserDefault(keyPath: \\.userColorScheme)\n    var userColorScheme: String\n\n    @SwiftyUserDefault(keyPath: \\.userThemeName, options: .cached)\n    var userThemeName: String?\n\n    @SwiftyUserDefault(keyPath: \\.userLastLoginDate, options: [.cached, .observed])\n    var userLastLoginDate: Date?\n}\n```\n\n## KVO\n\nKVO is supported for all the types that are `DefaultsSerializable`. However, if you have a custom type, it needs to have correctly defined bridges and serialization in them.\n\nTo observe a value for local DefaultsKey:\n```swift\nlet nameKey = DefaultsKey\u003CString>(\"name\", defaultValue: \"\")\nDefaults.observe(key: nameKey) { update in\n\t\u002F\u002F here you can access `oldValue`\u002F`newValue` and few other properties\n}\n```\n\nTo observe a value for a key defined in DefaultsKeys extension:\n```swift\nDefaults.observe(\\.nameKey) { update in\n\t\u002F\u002F here you can access `oldValue`\u002F`newValue` and few other properties\n}\n```\n\n\nBy default we are using `[.old, .new]` options for observing, but you can provide your own:\n```swift\nDefaults.observe(key: nameKey, options: [.initial, .old, .new]) { _ in }\n```\n\n## KeyPath dynamicMemberLookup\n\nSwiftyUserDefaults makes KeyPath dynamicMemberLookup usable in Swift 5.1!\n\n```swift\nextension DefaultsKeys {\n    var username: DefaultsKey\u003CString?> { .init(\"username\") }\n    var launchCount: DefaultsKey\u003CInt> { .init(\"launchCount\", defaultValue: 0) }\n}\n```\n\nAnd just use it ;-)\n\n```swift\n\u002F\u002F Get and set user defaults easily\nlet username = Defaults.username\nDefaults.hotkeyEnabled = true\n\n\u002F\u002F Modify value types in place\nDefaults.launchCount += 1\nDefaults.volume -= 0.1\nDefaults.strings += \"… can easily be extended!\"\n\n\u002F\u002F Use and modify typed arrays\nDefaults.libraries.append(\"SwiftyUserDefaults\")\nDefaults.libraries[0] += \" 2.0\"\n\n\u002F\u002F Easily work with custom serialized types\nDefaults.color = NSColor.white\nDefaults.color?.whiteComponent \u002F\u002F => 1.0\n```\n\n## Launch arguments\n\nDo you like to customize your app\u002Fscript\u002Ftests by UserDefaults? Now it's fully supported on our side, statically typed of course.\n\n_Note: for now we support only `Bool`, `Double`, `Int`, `String` values, but if you have any other requests for that feature, please open an issue or PR and we can talk about implementing it in new versions._\n\n### You can pass your arguments in your schema:\n\u003Cimg src=\"https:\u002F\u002Fi.imgur.com\u002FSDpOBpK.png\" alt=\"Pass launch arguments in Xcode Schema editor.\" \u002F>\n\n### Or you can use launch arguments in XCUIApplication:\n```swift\nfunc testExample() {\n    let app = XCUIApplication()\n    app.launchArguments = [\"-skipLogin\", \"true\", \"-loginTries\", \"3\", \"-lastGameTime\", \"61.3\", \"-nickname\", \"sunshinejr\"]\n    app.launch()\n}\n```\n### Or pass them as command line arguments!\n```bash\n.\u002Fscript -skipLogin true -loginTries 3 -lastGameTime 61.3 -nickname sunshinejr\n```\n\n## Utils\n\n### Remove all keys\n\nTo reset user defaults, use `removeAll` method.\n\n```swift\nDefaults.removeAll()\n```\n\n### Shared user defaults\n\nIf you're sharing your user defaults between different apps or an app and its extensions, you can use SwiftyUserDefaults by overriding the `Defaults` shortcut with your own. Just add in your app:\n\n```swift\nvar Defaults = DefaultsAdapter\u003CDefaultsKeys>(defaults: UserDefaults(suiteName: \"com.my.app\")!, keyStore: .init())\n```\n\n### Check key\n\nIf you want to check if we've got a value for `DefaultsKey`:\n```swift\nlet hasKey = Defaults.hasKey(\\.skipLogin)\n```\n\n## Installation\n\n### Requirements\n**Swift** version **>= 4.1**\u003Cbr \u002F>\n**iOS** version **>= 9.0**\u003Cbr \u002F>\n**macOS** version **>= 10.11**\u003Cbr \u002F>\n**tvOS** version **>= 9.0**\u003Cbr \u002F>\n**watchOS** version **>= 2.0**\n\n### CocoaPods\n\nIf you're using CocoaPods, just add this line to your Podfile:\n\n```ruby\npod 'SwiftyUserDefaults', '~> 5.0'\n```\n\nInstall by running this command in your terminal:\n\n```sh\npod install\n```\n\nThen import the library in all files where you use it:\n\n```swift\nimport SwiftyUserDefaults\n```\n\n### Carthage\n\nJust add to your Cartfile:\n\n```ruby\ngithub \"sunshinejr\u002FSwiftyUserDefaults\" ~> 5.0\n```\n\n### Swift Package Manager\n\nJust add to your `Package.swift` under dependencies:\n```swift\nlet package = Package(\n    name: \"MyPackage\",\n    products: [...],\n    dependencies: [\n        .package(url: \"https:\u002F\u002Fgithub.com\u002Fsunshinejr\u002FSwiftyUserDefaults.git\", .upToNextMajor(from: \"5.0.0\"))\n    ],\n    targets: [...]\n)\n```\n\n## More like this\n\nIf you like SwiftyUserDefaults, check out [SwiftyTimer](https:\u002F\u002Fgithub.com\u002Fradex\u002FSwiftyTimer), which applies the same swifty approach to `NSTimer`.\n\nYou might also be interested in my blog posts which explain the design process behind those libraries:\n- [Swifty APIs: NSUserDefaults](http:\u002F\u002Fradex.io\u002Fswift\u002Fnsuserdefaults\u002F)\n- [Statically-typed NSUserDefaults](http:\u002F\u002Fradex.io\u002Fswift\u002Fnsuserdefaults\u002Fstatic)\n- [Swifty APIs: NSTimer](http:\u002F\u002Fradex.io\u002Fswift\u002Fnstimer\u002F)\n- [Swifty methods](http:\u002F\u002Fradex.io\u002Fswift\u002Fmethods\u002F)\n\n## Contributing\n\nIf you have comments, complaints or ideas for improvements, feel free to open an issue or a pull request.\n\n## Authors and license\n\n*Maintainer:* Łukasz Mróz\n* [github.com\u002Fsunshinejr](http:\u002F\u002Fgithub.com\u002Fsunshinejr)\n* [twitter.com\u002Fthesunshinejr](http:\u002F\u002Ftwitter.com\u002Fthesunshinejr)\n* [sunshinejr.com](https:\u002F\u002Fsunshinejr.com)\n\n*Created by:* Radek Pietruszewski\n\n* [github.com\u002Fradex](http:\u002F\u002Fgithub.com\u002Fradex)\n* [twitter.com\u002Fradexp](http:\u002F\u002Ftwitter.com\u002Fradexp)\n* [radex.io](http:\u002F\u002Fradex.io)\n* this.is@radex.io\n\nSwiftyUserDefaults is available under the MIT license. See the LICENSE file for more info.\n","SwiftyUserDefaults 是一个为 NSUserDefaults 提供现代化 Swift API 的库。其核心功能包括定义键值对、使用值类型以及提供静态类型检查，从而使得用户默认设置的管理更加安全便捷。通过简洁的语法糖，开发者可以轻松地读写用户偏好设置，并支持多种数据类型的序列化和反序列化。此外，它还提供了属性包装器、KVO 支持等功能，增强了代码的可维护性和扩展性。适用于 iOS、macOS、watchOS 和 tvOS 平台的应用开发场景，特别适合需要高效处理用户偏好设置的项目。",2,"2026-06-11 03:09:13","top_language"]