默认设置 4.2.2

默认设置 4.2.2

测试已测试
Lang语言 Obj-CObjective C
许可证 MIT
发布最后发布2021年4月

Sindre Sorhus维护。



Defaults

Swifty和现代的UserDefaults

存储持久化的键值对跨应用启动。

它底层使用 NSUserDefaults,但提供了一个类型安全的封装,具有许多便利的功能。

它被诸如GifskiDatoLungoBattery IndicatorHEIC Converter等应用在生产中使用。

有关实际示例,请参阅我的Plash 应用

突出特点

  • 强类型:您事先声明类型和默认值。
  • 编码支持:您可以存储任何 Codable 值,例如一个枚举。
  • NSSecureCoding支持:您可以存储任何 NSSecureCoding 值。
  • SwiftUI: 属性包装器,当 UserDefaults 的值更改时更新视图。
  • 发布者: 内置构建了 Combine 发布者。
  • 观察:观察键的变化。
  • 可调试: 数据存储为 JSON-序列化的值。

兼容性

  • macOS 10.12+
  • iOS 10+
  • tvOS 10+
  • watchOS 3+

安装

Swift 包管理器

在 Xcode 的 "Swift 包管理器”标签页 中添加 https://github.com/sindresorhus/Defaults

Carthage

github "sindresorhus/Defaults"

CocoaPods

pod 'Defaults'

使用方法

您可以先声明带有类型和默认值的 defaults 键。

import Cocoa
import Defaults

extension Defaults.Keys {
	static let quality = Key<Double>("quality", default: 0.8)
	//            ^            ^         ^                ^
	//           Key          Type   UserDefaults name   Default value
}

然后,您可以将它们作为 Defaults 全局变量的下标来访问。

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']

您还可以声明可选键,当您不想提前声明默认值时。

extension Defaults.Keys {
	static let name = Key<Double?>("name")
}

if let name = Defaults[.name] {
	print(name)
}

默认值将是 nil


如果您有想要保存的 NSSecureCoding 类,可以使用以下方式

extension Defaults.Keys {
	static let someSecureCoding = NSSecureCodingKey<SomeNSSecureCodingClass>("someSecureCoding", default: SomeNSSecureCodingClass(string: "Default", int: 5, bool: true))
	static let someOptionalSecureCoding = NSSecureCodingOptionalKey<Double>("someOptionalSecureCoding")
}

Defaults[.someSecureCoding].string
//=> "Default"

Defaults[.someSecureCoding].int
//=> 5

Defaults[.someSecureCoding].bool
//=> true

您可以用在其他所有示例中的那些键一样使用它们。返回值将是您的 NSSecureCoding 类。

枚举示例

enum DurationKeys: String, Codable {
	case tenMinutes = "10 Minutes"
	case halfHour = "30 Minutes"
	case oneHour = "1 Hour"
}

extension Defaults.Keys {
	static let defaultDuration = Key<DurationKeys>("defaultDuration", default: .oneHour)
}

Defaults[.defaultDuration].rawValue
//=> "1 Hour"

直接使用按键

不要求将按键附加到Defaults.Keys

let isUnicorn = Defaults.Key<Bool>("isUnicorn", default: true)

Defaults[isUnicorn]
//=> true

SwiftUI支持

可以使用@Default属性包装器来获取/设置Defaults项,并在值更改时更新视图。这类似于@State

extension Defaults.Keys {
	static let hasUnicorn = Key<Bool>("hasUnicorn", default: false)
}

struct ContentView: View {
	@Default(.hasUnicorn) var hasUnicorn

	var body: some View {
		Text("Has Unicorn: \(hasUnicorn)")
		Toggle("Toggle Unicorn", isOn: $hasUnicorn)
	}
}

注意是指@Default,而不是@Defaults

不能在ObservableObject中使用@Default。它意味着要在View中使用。

这仅适用于Defaults.Key。如果您需要,欢迎提交针对Defaults.NSSecureCoding的PR。

观察某个键的变化

extension Defaults.Keys {
	static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
}

let observer = Defaults.observe(.isUnicornMode) { change in
	// Initial event
	print(change.oldValue)
	//=> false
	print(change.newValue)
	//=> false

	// First actual event
	print(change.oldValue)
	//=> false
	print(change.newValue)
	//=> true
}

Defaults[.isUnicornMode] = true

与原生的UserDefaults键观察相比,这里您会收到一个强类型的更改对象。

还有一个使用Combine框架的观察API,公开了一个针对键更改的发布者

let publisher = Defaults.publisher(.isUnicornMode)

let cancellable = publisher.sink { change in
	// Initial event
	print(change.oldValue)
	//=> false
	print(change.newValue)
	//=> false

	// First actual event
	print(change.oldValue)
	//=> false
	print(change.newValue)
	//=> true
}

Defaults[.isUnicornMode] = true

// To invalidate the observation.
cancellable.cancel()

自动失效观察

extension Defaults.Keys {
	static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
}

final class Foo {
	init() {
		Defaults.observe(.isUnicornMode) { change in
			print(change.oldValue)
			print(change.newValue)
		}.tieToLifetime(of: self)
	}
}

Defaults[.isUnicornMode] = true

观察将在self被初始化后有效。

将键重置为其默认值

extension Defaults.Keys {
	static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
}

Defaults[.isUnicornMode] = true
//=> true

Defaults.reset(.isUnicornMode)

Defaults[.isUnicornMode]
//=> false

这也适用于具有可选类型的Key,它将被重置回nil

控制事件传播

Defaults.withoutPropagation闭包中做出的更改不会传播到观察回调(Defaults.observe()Defaults.publisher()),因此可能会防止无限递归。

let observer = Defaults.observe(keys: .key1, .key2) {
		//

		Defaults.withoutPropagation {
			// Update `.key1` without propagating the change to listeners.
			Defaults[.key1] = 11
		}

		// This will be propagated.
		Defaults[.someKey] = true
	}

这就像是带有糖的 UserDefaults

这也行

extension Defaults.Keys {
	static let isUnicorn = Key<Bool>("isUnicorn", default: true)
}

UserDefaults.standard[.isUnicorn]
//=> true

共享 UserDefaults

let extensionDefaults = UserDefaults(suiteName: "com.unicorn.app")!

extension Defaults.Keys {
	static let isUnicorn = Key<Bool>("isUnicorn", default: true, suite: extensionDefaults)
}

Defaults[.isUnicorn]
//=> true

// Or

extensionDefaults[.isUnicorn]
//=> true

默认值是使用 UserDefaults 注册的

当你创建一个 Defaults.Key 时,它会自动将 default 值注册到普通的 UserDefaults 中。这意味着你可以在 Interface Builder 中的绑定等地方使用默认值。

extension Defaults.Keys {
	static let isUnicornMode = Key<Bool>("isUnicornMode", default: true)
}

print(UserDefaults.standard.bool(forKey: Defaults.Keys.isUnicornMode.name))
//=> true

API

Defaults

Defaults.Keys

类型:

存储键。

Defaults.Key (别名为 Defaults.Keys.Key)

Defaults.Key<T>(_ key: String, default: T, suite: UserDefaults = .standard)

类型:

创建一个具有默认值的键。

默认值被写入实际的 UserDefaults,并可在其他地方使用。例如,使用 Interface Builder 绑定。

Defaults.NSSecureCodingKey (别名为 Defaults.Keys.NSSecureCodingKey)

Defaults.NSSecureCodingKey<T>(_ key: String, default: T, suite: UserDefaults = .standard)

类型:

创建一个带有默认值的 NSSecureCoding 键。

默认值被写入实际的 UserDefaults,并可在其他地方使用。例如,使用 Interface Builder 绑定。

Defaults.NSSecureCodingOptionalKey (别名为 Defaults.Keys.NSSecureCodingOptionalKey)

Defaults.NSSecureCodingOptionalKey<T>(_ key: String, suite: UserDefaults = .standard)

类型:

创建一个带有可选值的 NSSecureCoding 键。

Defaults.reset(keys…)

类型:func

将给定的键重置为其默认值。

您可以指定最多 10 个键。如果需要指定更多,请多次调用此方法。

您还可以指定字符串键,这在您需要将某些键存储在集合中时非常有用,因为无法将 Defaults.Key 存储在集合中,因为它具有泛型。

Defaults.observe

Defaults.observe<T: Codable>(
	_ key: Defaults.Key<T>,
	options: ObservationOptions = [.initial],
	handler: @escaping (KeyChange<T>) -> Void
) -> Defaults.Observation
Defaults.observe<T: NSSecureCoding>(
	_ key: Defaults.NSSecureCodingKey<T>,
	options: ObservationOptions = [.initial],
	handler: @escaping (NSSecureCodingKeyChange<T>) -> Void
) -> Defaults.Observation
Defaults.observe<T: NSSecureCoding>(
	_ key: Defaults.NSSecureCodingOptionalKey<T>,
	options: ObservationOptions = [.initial],
	handler: @escaping (NSSecureCodingOptionalKeyChange<T>) -> Void
) -> Defaults.Observation

类型:func

观察一个或可选键的变化。

默认情况下,它还会在创建时触发一个初始事件。这可以用于为控件设置默认值。您可以通过options参数覆盖此行为。

Defaults.observe(keys: keys..., options:)

类型:func

观察任意类型多个键的变化,但不包括有关变化的信息。

选项与单个键的.observe(…)中的选项相同。

Defaults.publisher(_ key:, options:)

Defaults.publisher<T: Codable>(
	_ key: Defaults.Key<T>,
	options: ObservationOptions = [.initial]
) -> AnyPublisher<KeyChange<T>, Never>
Defaults.publisher<T: NSSecureCoding>(
	_ key: Defaults.NSSecureCodingKey<T>,
	options: ObservationOptions = [.initial]
) -> AnyPublisher<NSSecureCodingKeyChange<T>, Never>
Defaults.publisher<T: NSSecureCoding>(
	_ key: Defaults.NSSecureCodingOptionalKey<T>,
	options: ObservationOptions = [.initial]
) -> AnyPublisher<NSSecureCodingOptionalKeyChange<T>, Never>

类型:func

使用来自Combine框架的Publisher实现观察API。

在macOS 10.15+、iOS 13.0+、tvOS 13.0+和watchOS 6.0+上可用。

Defaults.publisher(keys: keys…, options:)

类型:func

Combine观察API,用于多个键的观察,但不提供变化的具体信息。

在macOS 10.15+、iOS 13.0+、tvOS 13.0+和watchOS 6.0+上可用。

Defaults.removeAll

Defaults.removeAll(suite: UserDefaults = .standard)

类型:func

从给定的UserDefaults suite中移除所有条目。

Defaults.Observation

类型:协议

表示对默认键的一次观察。

Defaults.Observation#invalidate

Defaults.Observation#invalidate()

类型:func

使观察无效。

Defaults.Observation#tieToLifetime

@discardableResult
Defaults.Observation#tieToLifetime(of weaklyHeldObject: AnyObject) -> Self

类型:func

使观察与另一个对象存在相同时间保持活动状态。

当:weaklyHeldObject被初始化时,观察自动变为无效。

Defaults.Observation.removeLifetimeTie

Defaults.Observation#removeLifetimeTie()

类型:func

如果存在,则通过tieToLifetime(of:)创建的生存时间绑定打破。

tieToLifetime(of:)的任何调用效果都将被反转。注意,但是,如果绑定的对象已经死亡,则观察已经无效,此方法没有逻辑上的效果。

Defaults.withoutPropagation(_ closure:)

在不会触发更改事件的情况下执行闭包。

在闭包内部所做的任何Defaults键更改都不会传播到Defaults事件监听器(《Defaults.observe()》和《Defaults.publisher()》)。这可以用来防止在想要在监听同一键的回调中更改键时发生无限递归。

@Default(_ key:)

获取/设置了一个Defaults项,并且在值更改时也会更新视图。

这仅适用于Defaults.Key。如果您需要,欢迎提交针对Defaults.NSSecureCoding的PR。

常见问题

我如何存储一个包含任意值的字典?

您不能直接存储 [String: Any],因为它无法遵守 Codable 规范。但是,您可以使用 AnyCodable 包来解决这个问题。

import AnyCodable

extension Defaults.Keys {
	static let magic = Key<[String: AnyCodable]>("magic", default: [:])
}

//

Defaults[.magic]["unicorn"] = "🦄"

if let value = Defaults[.magic]["unicorn"]?.value {
	print(value)
	//=> "🦄"
}

Defaults[.magic]["number"] = 3
Defaults[.magic]["boolean"] = true

这与 SwiftyUserDefaults 有何不同?

它受到了该包和其他解决方案的启发。主要区别在于,此模块没有设置硬编码的默认值,并自带 Codable 支持。

维护者

相关

  • Preferences - 向您的 macOS 应用添加偏好设置窗口
  • KeyboardShortcuts - 向您的 macOS 应用添加用户自定义的全局快捷键
  • LaunchAtLogin - 向您的 macOS 应用添加“登录时启动”功能
  • DockProgress - 在您的应用Dock图标中显示进度条
  • Gifski - 在您的Mac上将视频转换为高质量GIF
  • 更多...