Improve documentation

This commit is contained in:
Sindre Sorhus 2022-11-18 15:42:31 +07:00
parent 35943225a0
commit 60013d90e2
14 changed files with 299 additions and 165 deletions

View File

@ -11,7 +11,7 @@ extension Defaults {
`get` will deserialize the internal value to the type that user specify in the function parameter. `get` will deserialize the internal value to the type that user specify in the function parameter.
``` ```swift
let any = Defaults.Key<Defaults.AnySerializable>("independentAnyKey", default: 121_314) let any = Defaults.Key<Defaults.AnySerializable>("independentAnyKey", default: 121_314)
print(Defaults[any].get(Int.self)) print(Defaults[any].get(Int.self))
@ -20,7 +20,7 @@ extension Defaults {
- Note: The only way to assign a non-serializable value is using `ExpressibleByArrayLiteral` or `ExpressibleByDictionaryLiteral` to assign a type that is not a `UserDefaults` natively supported type. - Note: The only way to assign a non-serializable value is using `ExpressibleByArrayLiteral` or `ExpressibleByDictionaryLiteral` to assign a type that is not a `UserDefaults` natively supported type.
``` ```swift
private enum mime: String, Defaults.Serializable { private enum mime: String, Defaults.Serializable {
case JSON = "application/json" case JSON = "application/json"
} }

View File

@ -1,25 +1,9 @@
import Foundation import Foundation
/** public protocol _DefaultsSerializable {
Types that conform to this protocol can be used with `Defaults`.
The type should have a static variable `bridge` which should reference an instance of a type that conforms to `Defaults.Bridge`.
```
struct User {
username: String
password: String
}
extension User: Defaults.Serializable {
static let bridge = UserBridge()
}
```
*/
public protocol DefaultsSerializable {
typealias Value = Bridge.Value typealias Value = Bridge.Value
typealias Serializable = Bridge.Serializable typealias Serializable = Bridge.Serializable
associatedtype Bridge: DefaultsBridge associatedtype Bridge: Defaults.Bridge
/** /**
Static bridge for the `Value` which cannot be stored natively. Static bridge for the `Value` which cannot be stored natively.
@ -32,59 +16,7 @@ public protocol DefaultsSerializable {
static var isNativelySupportedType: Bool { get } static var isNativelySupportedType: Bool { get }
} }
/** public protocol _DefaultsBridge {
A `Bridge` is responsible for serialization and deserialization.
It has two associated types `Value` and `Serializable`.
- `Value`: The type you want to use.
- `Serializable`: The type stored in `UserDefaults`.
- `serialize`: Executed before storing to the `UserDefaults` .
- `deserialize`: Executed after retrieving its value from the `UserDefaults`.
```
struct User {
username: String
password: String
}
extension User {
static let bridge = UserBridge()
}
struct UserBridge: Defaults.Bridge {
typealias Value = User
typealias Serializable = [String: String]
func serialize(_ value: Value?) -> Serializable? {
guard let value else {
return nil
}
return [
"username": value.username,
"password": value.password
]
}
func deserialize(_ object: Serializable?) -> Value? {
guard
let object,
let username = object["username"],
let password = object["password"]
else {
return nil
}
return User(
username: username,
password: password
)
}
}
```
*/
public protocol DefaultsBridge {
associatedtype Value associatedtype Value
associatedtype Serializable associatedtype Serializable
@ -92,45 +24,27 @@ public protocol DefaultsBridge {
func deserialize(_ object: Serializable?) -> Value? func deserialize(_ object: Serializable?) -> Value?
} }
public protocol DefaultsCollectionSerializable: Collection, Defaults.Serializable { public protocol _DefaultsCollectionSerializable: Collection, Defaults.Serializable {
/** /**
`Collection` does not have a initializer, but we need a initializer to convert an array into the `Value`. `Collection` does not have a initializer, but we need a initializer to convert an array into the `Value`.
*/ */
init(_ elements: [Element]) init(_ elements: [Element])
} }
public protocol DefaultsSetAlgebraSerializable: SetAlgebra, Defaults.Serializable { public protocol _DefaultsSetAlgebraSerializable: SetAlgebra, Defaults.Serializable {
/** /**
Since `SetAlgebra` protocol does not conform to `Sequence`, we cannot convert a `SetAlgebra` to an `Array` directly. Since `SetAlgebra` protocol does not conform to `Sequence`, we cannot convert a `SetAlgebra` to an `Array` directly.
*/ */
func toArray() -> [Element] func toArray() -> [Element]
} }
/** public protocol _DefaultsCodableBridge: Defaults.Bridge where Serializable == String, Value: Codable {}
Convenience protocol for `Codable`.
*/
public protocol DefaultsCodableBridge: Defaults.Bridge where Serializable == String, Value: Codable {}
/** public protocol _DefaultsPreferRawRepresentable: RawRepresentable {}
Ambiguous bridge selector protocol. This lets you select your preferred bridge when there are multiple possibilities. public protocol _DefaultsPreferNSSecureCoding: NSSecureCoding {}
For example:
```
enum Interval: Int, Codable, Defaults.Serializable, Defaults.PreferRawRepresentable {
case tenMinutes = 10
case halfHour = 30
case oneHour = 60
}
```
By default, if an `enum` conforms to `Codable` and `Defaults.Serializable`, it will use the `CodableBridge`, but by conforming to `Defaults.PreferRawRepresentable`, we can switch the bridge back to `RawRepresentableBridge`.
*/
public protocol DefaultsPreferRawRepresentable: RawRepresentable {}
public protocol DefaultsPreferNSSecureCoding: NSSecureCoding {}
// Essential properties for serializing and deserializing `ClosedRange` and `Range`. // Essential properties for serializing and deserializing `ClosedRange` and `Range`.
public protocol DefaultsRange { public protocol _DefaultsRange {
associatedtype Bound: Comparable, Defaults.Serializable associatedtype Bound: Comparable, Defaults.Serializable
var lowerBound: Bound { get } var lowerBound: Bound { get }

View File

@ -2,18 +2,45 @@
import Foundation import Foundation
public enum Defaults { public enum Defaults {
public typealias Keys = AnyKey /**
public typealias Serializable = DefaultsSerializable Access stored values.
public typealias CollectionSerializable = DefaultsCollectionSerializable
public typealias SetAlgebraSerializable = DefaultsSetAlgebraSerializable
public typealias PreferRawRepresentable = DefaultsPreferRawRepresentable
public typealias PreferNSSecureCoding = DefaultsPreferNSSecureCoding
public typealias Bridge = DefaultsBridge
public typealias RangeSerializable = DefaultsRange & DefaultsSerializable
typealias CodableBridge = DefaultsCodableBridge
```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
Defaults[.quality] += 0.1
//=> 0.6
Defaults[.quality] = "🦄"
//=> [Cannot assign value of type 'String' to type 'Double']
```
*/
public static subscript<Value: Serializable>(key: Key<Value>) -> Value {
get { key.suite[key] }
set {
key.suite[key] = newValue
}
}
}
extension Defaults {
// We cannot use `Key` as the container for keys because of "Static stored properties not supported in generic types". // We cannot use `Key` as the container for keys because of "Static stored properties not supported in generic types".
public class AnyKey { /**
Type-erased key.
*/
public class _AnyKey {
public typealias Key = Defaults.Key public typealias Key = Defaults.Key
public let name: String public let name: String
@ -31,8 +58,25 @@ public enum Defaults {
suite.removeObject(forKey: name) suite.removeObject(forKey: name)
} }
} }
}
public final class Key<Value: Serializable>: AnyKey { extension Defaults {
/**
Strongly-typed key used to access values.
You declare the defaults keys upfront with a type and default value.
```swift
import Defaults
extension Defaults.Keys {
static let quality = Key<Double>("quality", default: 0.8)
// ^ ^ ^ ^
// Key Type UserDefaults name Default value
}
```
*/
public final class Key<Value: Serializable>: _AnyKey {
/** /**
It will be executed in these situations: It will be executed in these situations:
@ -50,7 +94,11 @@ public enum Defaults {
The `default` parameter should not be used if the `Value` type is an optional. The `default` parameter should not be used if the `Value` type is an optional.
*/ */
public init(_ key: String, default defaultValue: Value, suite: UserDefaults = .standard) { public init(
_ key: String,
default defaultValue: Value,
suite: UserDefaults = .standard
) {
self.defaultValueGetter = { defaultValue } self.defaultValueGetter = { defaultValue }
super.init(name: key, suite: suite) super.init(name: key, suite: suite)
@ -78,19 +126,30 @@ public enum Defaults {
} }
``` ```
- Parameter key: The key must be ASCII, not start with `@`, and cannot contain a dot (`.`).
- Note: This initializer will not set the default value in the actual `UserDefaults`. This should not matter much though. It's only really useful if you use legacy KVO bindings. - Note: This initializer will not set the default value in the actual `UserDefaults`. This should not matter much though. It's only really useful if you use legacy KVO bindings.
*/ */
public init(_ key: String, suite: UserDefaults = .standard, default defaultValueGetter: @escaping () -> Value) { public init(
_ key: String,
suite: UserDefaults = .standard,
default defaultValueGetter: @escaping () -> Value
) {
self.defaultValueGetter = defaultValueGetter self.defaultValueGetter = defaultValueGetter
super.init(name: key, suite: suite) super.init(name: key, suite: suite)
} }
}
public static subscript<Value: Serializable>(key: Key<Value>) -> Value { /**
get { key.suite[key] } Create a defaults key with an optional value.
set {
key.suite[key] = newValue - Parameter key: The key must be ASCII, not start with `@`, and cannot contain a dot (`.`).
*/
public convenience init<T>(
_ key: String,
suite: UserDefaults = .standard
) where Value == T? {
self.init(key, default: nil, suite: suite)
} }
} }
} }
@ -106,27 +165,122 @@ extension Defaults {
} }
} }
extension Defaults.Key { extension Defaults._AnyKey: Equatable {
/** public static func == (lhs: Defaults._AnyKey, rhs: Defaults._AnyKey) -> Bool {
Create a defaults key.
- Parameter key: The key must be ASCII, not start with `@`, and cannot contain a dot (`.`).
*/
public convenience init<T>(_ key: String, suite: UserDefaults = .standard) where Value == T? {
self.init(key, default: nil, suite: suite)
}
}
extension Defaults.AnyKey: Equatable {
public static func == (lhs: Defaults.AnyKey, rhs: Defaults.AnyKey) -> Bool {
lhs.name == rhs.name lhs.name == rhs.name
&& lhs.suite == rhs.suite && lhs.suite == rhs.suite
} }
} }
extension Defaults.AnyKey: Hashable { extension Defaults._AnyKey: Hashable {
public func hash(into hasher: inout Hasher) { public func hash(into hasher: inout Hasher) {
hasher.combine(name) hasher.combine(name)
hasher.combine(suite) hasher.combine(suite)
} }
} }
extension Defaults {
public typealias Keys = _AnyKey
/**
Types that conform to this protocol can be used with `Defaults`.
The type should have a static variable `bridge` which should reference an instance of a type that conforms to `Defaults.Bridge`.
```swift
struct User {
username: String
password: String
}
extension User: Defaults.Serializable {
static let bridge = UserBridge()
}
```
*/
public typealias Serializable = _DefaultsSerializable
public typealias CollectionSerializable = _DefaultsCollectionSerializable
public typealias SetAlgebraSerializable = _DefaultsSetAlgebraSerializable
/**
Ambiguous bridge selector protocol that lets you select your preferred bridge when there are multiple possibilities.
```swift
enum Interval: Int, Codable, Defaults.Serializable, Defaults.PreferRawRepresentable {
case tenMinutes = 10
case halfHour = 30
case oneHour = 60
}
```
By default, if an `enum` conforms to `Codable` and `Defaults.Serializable`, it will use the `CodableBridge`, but by conforming to `Defaults.PreferRawRepresentable`, we can switch the bridge back to `RawRepresentableBridge`.
*/
public typealias PreferRawRepresentable = _DefaultsPreferRawRepresentable
/**
Ambiguous bridge selector protocol that lets you select your preferred bridge when there are multiple possibilities.
*/
public typealias PreferNSSecureCoding = _DefaultsPreferNSSecureCoding
/**
A `Bridge` is responsible for serialization and deserialization.
It has two associated types `Value` and `Serializable`.
- `Value`: The type you want to use.
- `Serializable`: The type stored in `UserDefaults`.
- `serialize`: Executed before storing to the `UserDefaults` .
- `deserialize`: Executed after retrieving its value from the `UserDefaults`.
```swift
struct User {
username: String
password: String
}
extension User {
static let bridge = UserBridge()
}
struct UserBridge: Defaults.Bridge {
typealias Value = User
typealias Serializable = [String: String]
func serialize(_ value: Value?) -> Serializable? {
guard let value else {
return nil
}
return [
"username": value.username,
"password": value.password
]
}
func deserialize(_ object: Serializable?) -> Value? {
guard
let object,
let username = object["username"],
let password = object["password"]
else {
return nil
}
return User(
username: username,
password: password
)
}
}
```
*/
public typealias Bridge = _DefaultsBridge
public typealias RangeSerializable = _DefaultsRange & _DefaultsSerializable
/**
Convenience protocol for `Codable`.
*/
typealias CodableBridge = _DefaultsCodableBridge
}

View File

@ -0,0 +1,60 @@
# ``Defaults``
Store key-value pairs persistently across launches of your app.
It uses [`UserDefaults`](https://developer.apple.com/documentation/foundation/userdefaults) underneath but exposes a type-safe facade with lots of nice conveniences.
## Usage
You declare the defaults keys upfront with a type and default value.
```swift
import Defaults
extension Defaults.Keys {
static let quality = Key<Double>("quality", default: 0.8)
// ^ ^ ^ ^
// Key Type UserDefaults name Default value
}
```
You can then access it as a subscript on the `Defaults` global:
```swift
Defaults[.quality]
//=> 0.8
Defaults[.quality] = 0.5
//=> 0.5
```
[Learn More](https://github.com/sindresorhus/Defaults#usage)
## Topics
### Essentials
- ``Defaults/subscript(_:)``
- ``Defaults/Key``
- ``Defaults/Serializable``
### Methods
- ``Defaults/reset(_:)-7jv5v``
- ``Defaults/reset(_:)-7es1e``
- ``Defaults/removeAll(suite:)``
### SwiftUI
- ``Default``
- ``Defaults/Toggle``
### Events
- ``Defaults/publisher(_:options:)``
- ``Defaults/publisher(keys:options:)``
### Force Type Resolution
- ``Defaults/PreferRawRepresentable``
- ``Defaults/PreferNSSecureCoding``

View File

@ -8,7 +8,7 @@ extension Defaults {
/** /**
Migrate the given key's value from JSON string to `Value`. Migrate the given key's value from JSON string to `Value`.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let array = Key<Set<String>?>("array") static let array = Key<Set<String>?>("array")
} }

View File

@ -2,8 +2,8 @@ import Foundation
import CoreGraphics import CoreGraphics
extension Defaults { extension Defaults {
public typealias NativeType = DefaultsNativeType public typealias NativeType = _DefaultsNativeType
public typealias CodableType = DefaultsCodableType public typealias CodableType = _DefaultsCodableType
} }
extension Data: Defaults.NativeType { extension Data: Defaults.NativeType {

View File

@ -9,7 +9,7 @@ It should have an associated type name `CodableForm` where its protocol conform
So we can convert the JSON string into a `NativeType` like this: So we can convert the JSON string into a `NativeType` like this:
``` ```swift
guard guard
let jsonData = string?.data(using: .utf8), let jsonData = string?.data(using: .utf8),
let codable = try? JSONDecoder().decode(NativeType.CodableForm.self, from: jsonData) let codable = try? JSONDecoder().decode(NativeType.CodableForm.self, from: jsonData)
@ -20,7 +20,7 @@ else {
return codable.toNative() return codable.toNative()
``` ```
*/ */
public protocol DefaultsNativeType: Defaults.Serializable { public protocol _DefaultsNativeType: Defaults.Serializable {
associatedtype CodableForm: Defaults.CodableType associatedtype CodableForm: Defaults.CodableType
} }
@ -31,7 +31,7 @@ Represents the type before migration an its protocol should conform to `Codable`
The main purposed of `CodableType` is trying to infer the `Codable` type to do `JSONDecoder().decode`. It should have an associated type name `NativeForm` which is the type we want it to store in `UserDefaults`. nd it also have a `toNative()` function to convert itself into `NativeForm`. The main purposed of `CodableType` is trying to infer the `Codable` type to do `JSONDecoder().decode`. It should have an associated type name `NativeForm` which is the type we want it to store in `UserDefaults`. nd it also have a `toNative()` function to convert itself into `NativeForm`.
``` ```swift
struct User { struct User {
username: String username: String
password: String password: String
@ -55,7 +55,7 @@ extension CodableUser: Defaults.CodableType {
} }
``` ```
*/ */
public protocol DefaultsCodableType: Codable { public protocol _DefaultsCodableType: Codable {
associatedtype NativeForm: Defaults.NativeType associatedtype NativeForm: Defaults.NativeType
func toNative() -> NativeForm func toNative() -> NativeForm
} }

View File

@ -19,19 +19,19 @@ extension UserDefaults {
1. If `Value` is `[String]`, `Value.CodableForm` will covert into `[String].CodableForm`. 1. If `Value` is `[String]`, `Value.CodableForm` will covert into `[String].CodableForm`.
``` ```swift
JSONDecoder().decode([String].CodableForm.self, from: jsonData) JSONDecoder().decode([String].CodableForm.self, from: jsonData)
``` ```
2. If `Array` conforms to `NativeType`, its `CodableForm` is `[Element.CodableForm]` and `Element` is `String`. 2. If `Array` conforms to `NativeType`, its `CodableForm` is `[Element.CodableForm]` and `Element` is `String`.
``` ```swift
JSONDecoder().decode([String.CodableForm].self, from: jsonData) JSONDecoder().decode([String.CodableForm].self, from: jsonData)
``` ```
3. `String`'s `CodableForm` is `self`, because `String` is `Codable`. 3. `String`'s `CodableForm` is `self`, because `String` is `Codable`.
``` ```swift
JSONDecoder().decode([String].self, from: jsonData) JSONDecoder().decode([String].self, from: jsonData)
``` ```
*/ */

View File

@ -74,7 +74,7 @@ extension Defaults {
/** /**
Returns a type-erased `Publisher` that publishes changes related to the given key. Returns a type-erased `Publisher` that publishes changes related to the given key.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false) static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
} }
@ -103,7 +103,7 @@ extension Defaults {
*/ */
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *) @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
public static func publisher( public static func publisher(
keys: AnyKey..., keys: _AnyKey...,
options: ObservationOptions = [.initial] options: ObservationOptions = [.initial]
) -> AnyPublisher<Void, Never> { ) -> AnyPublisher<Void, Never> {
let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher() let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher()

View File

@ -1,12 +1,12 @@
import Foundation import Foundation
public protocol DefaultsObservation: AnyObject { public protocol _DefaultsObservation: AnyObject {
func invalidate() func invalidate()
/** /**
Keep this observation alive for as long as, and no longer than, another object exists. Keep this observation alive for as long as, and no longer than, another object exists.
``` ```swift
Defaults.observe(.xyz) { [unowned self] change in Defaults.observe(.xyz) { [unowned self] change in
self.xyz = change.newValue self.xyz = change.newValue
}.tieToLifetime(of: self) }.tieToLifetime(of: self)
@ -25,7 +25,7 @@ public protocol DefaultsObservation: AnyObject {
} }
extension Defaults { extension Defaults {
public typealias Observation = DefaultsObservation public typealias Observation = _DefaultsObservation
public enum ObservationOption { public enum ObservationOption {
/** /**
@ -95,7 +95,7 @@ extension Defaults {
- Note: This only works with `Defaults.observe()` and `Defaults.publisher()`. User-made KVO will not be affected. - Note: This only works with `Defaults.observe()` and `Defaults.publisher()`. User-made KVO will not be affected.
``` ```swift
let observer = Defaults.observe(keys: .key1, .key2) { let observer = Defaults.observe(keys: .key1, .key2) {
// //
@ -283,7 +283,7 @@ extension Defaults {
/** /**
Observe a defaults key. Observe a defaults key.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false) static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
} }
@ -312,7 +312,7 @@ extension Defaults {
/** /**
Observe multiple keys of any type, but without any information about the changes. Observe multiple keys of any type, but without any information about the changes.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let setting1 = Key<Bool>("setting1", default: false) static let setting1 = Key<Bool>("setting1", default: false)
static let setting2 = Key<Bool>("setting2", default: true) static let setting2 = Key<Bool>("setting2", default: true)
@ -324,7 +324,7 @@ extension Defaults {
``` ```
*/ */
public static func observe( public static func observe(
keys: AnyKey..., keys: _AnyKey...,
options: ObservationOptions = [.initial], options: ObservationOptions = [.initial],
handler: @escaping () -> Void handler: @escaping () -> Void
) -> Observation { ) -> Observation {

View File

@ -9,7 +9,7 @@ extension Defaults {
- Parameter keys: String keys to reset. - Parameter keys: String keys to reset.
- Parameter suite: `UserDefaults` suite. - Parameter suite: `UserDefaults` suite.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false) static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
} }
@ -36,7 +36,7 @@ extension Defaults {
- Parameter keys: String keys to reset. - Parameter keys: String keys to reset.
- Parameter suite: `UserDefaults` suite. - Parameter suite: `UserDefaults` suite.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false) static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
} }
@ -62,7 +62,7 @@ extension Defaults {
/** /**
Reset the given keys back to their default values. Reset the given keys back to their default values.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false) static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
} }
@ -76,14 +76,14 @@ extension Defaults {
//=> false //=> false
``` ```
*/ */
public static func reset(_ keys: AnyKey...) { public static func reset(_ keys: _AnyKey...) {
reset(keys) reset(keys)
} }
/** /**
Reset the given keys back to their default values. Reset the given keys back to their default values.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false) static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
} }
@ -97,7 +97,7 @@ extension Defaults {
//=> false //=> false
``` ```
*/ */
public static func reset(_ keys: [AnyKey]) { public static func reset(_ keys: [_AnyKey]) {
for key in keys { for key in keys {
key.reset() key.reset()
} }

View File

@ -62,6 +62,11 @@ extension Defaults {
} }
} }
/**
Access stored values from SwiftUI.
This is similar to `@AppStorage` but it accepts a ``Defaults/Key`` and many more types.
*/
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper @propertyWrapper
public struct Default<Value: Defaults.Serializable>: DynamicProperty { public struct Default<Value: Defaults.Serializable>: DynamicProperty {
@ -77,7 +82,7 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
- Important: You cannot use this in an `ObservableObject`. It's meant to be used in a `View`. - Important: You cannot use this in an `ObservableObject`. It's meant to be used in a `View`.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let hasUnicorn = Key<Bool>("hasUnicorn", default: false) static let hasUnicorn = Key<Bool>("hasUnicorn", default: false)
} }
@ -126,7 +131,7 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
/** /**
Reset the key back to its default value. Reset the key back to its default value.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let opacity = Key<Double>("opacity", default: 1) static let opacity = Key<Double>("opacity", default: 1)
} }
@ -158,11 +163,11 @@ extension Default where Value: Equatable {
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) @available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
extension Defaults { extension Defaults {
/** /**
Creates a SwiftUI `Toggle` view that is connected to a `Defaults` key with a `Bool` value. A SwiftUI `Toggle` view that is connected to a ``Defaults/Key`` with a `Bool` value.
The toggle works exactly like the SwiftUI `Toggle`. The toggle works exactly like the SwiftUI `Toggle`.
``` ```swift
extension Defaults.Keys { extension Defaults.Keys {
static let showAllDayEvents = Key<Bool>("showAllDayEvents", default: false) static let showAllDayEvents = Key<Bool>("showAllDayEvents", default: false)
} }
@ -176,7 +181,7 @@ extension Defaults {
You can also listen to changes: You can also listen to changes:
``` ```swift
struct ShowAllDayEventsSetting: View { struct ShowAllDayEventsSetting: View {
var body: some View { var body: some View {
Defaults.Toggle("Show All-Day Events", key: .showAllDayEvents) Defaults.Toggle("Show All-Day Events", key: .showAllDayEvents)

View File

@ -59,7 +59,7 @@ final class LifetimeAssociation {
When either the owner or the new `LifetimeAssociation` is destroyed, the given deinit handler, if any, is called. When either the owner or the new `LifetimeAssociation` is destroyed, the given deinit handler, if any, is called.
``` ```swift
class Ghost { class Ghost {
var association: LifetimeAssociation? var association: LifetimeAssociation?
@ -159,7 +159,7 @@ extension Defaults.Serializable {
Converts a natively supported type from `UserDefaults` into `Self`. Converts a natively supported type from `UserDefaults` into `Self`.
``` ```swift
guard let anyObject = object(forKey: key) else { guard let anyObject = object(forKey: key) else {
return nil return nil
} }
@ -188,7 +188,7 @@ extension Defaults.Serializable {
Converts `Self` into `UserDefaults` native support type. Converts `Self` into `UserDefaults` native support type.
``` ```swift
set(Value.toSerialize(value), forKey: key) set(Value.toSerialize(value), forKey: key)
``` ```
*/ */

View File

@ -4,7 +4,7 @@
Store key-value pairs persistently across launches of your app. Store key-value pairs persistently across launches of your app.
It uses `NSUserDefaults` underneath but exposes a type-safe facade with lots of nice conveniences. It uses `UserDefaults` underneath but exposes a type-safe facade with lots of nice conveniences.
It's used in production by apps like [Gifski](https://github.com/sindresorhus/Gifski), [Dato](https://sindresorhus.com/dato), [Lungo](https://sindresorhus.com/lungo), [Battery Indicator](https://sindresorhus.com/battery-indicator), and [HEIC Converter](https://sindresorhus.com/heic-converter). It's used in production by apps like [Gifski](https://github.com/sindresorhus/Gifski), [Dato](https://sindresorhus.com/dato), [Lungo](https://sindresorhus.com/lungo), [Battery Indicator](https://sindresorhus.com/battery-indicator), and [HEIC Converter](https://sindresorhus.com/heic-converter).
@ -78,12 +78,13 @@ If a type conforms to both `NSSecureCoding` and `Codable`, then `Codable` will b
## Usage ## Usage
You declare the defaults keys upfront with type and default value. [API documentation.](https://swiftpackageindex.com/sindresorhus/Defaults/documentation/defaults)
You declare the defaults keys upfront with a type and default value.
**The key name must be ASCII, not start with `@`, and cannot contain a dot (`.`).** **The key name must be ASCII, not start with `@`, and cannot contain a dot (`.`).**
```swift ```swift
import Cocoa
import Defaults import Defaults
extension Defaults.Keys { extension Defaults.Keys {