Swift Package Manager compatible MIT licensed

UserDefault

一个为 UserDefaults 设计的过度设计的属性包装器接口。

这个包最初的目的是编写一个访问 UserDefaults 的属性包装器。然而,由于我希望避免处理非直接属性列表可转换类型的所有开销,它很快就超越了最初的目的。

示例

直接存储

@UserDefault("hasSeenSplashScreen") var hasSeenSplashScreen = false
@UserDefault("name") var name: String?
@UserDefault("count") var count: Int? = 42

hasSeenSplashScreen = true
name = "John Appleseed"
count = nil

Codable/NSCoding

对于 Codable,有一个协议扩展,可以让你避免编写那些样板代码。

struct CodableStruct {
	var foo: String
}

extension CodableStruct: Equatable, Codable, DefaultsValueConvertible {}

@UserDefault("test") var test = CodableStruct(foo: "hello, world!")
test.foo = "goodbye."

以及对于 NSCoding

extension UIColor: DefaultsValueConvertible {}
@UserDefault("color") var color = UIColor.systemRed
color = .systemBlue

进一步的条件一致性

我不会在这里深入细节,但也存在针对 ArrayDictionaryCollectionOptionalRawRepresentable 的条件一致性(使其易于使用枚举)。目标是涵盖标准库中的所有合理情况,因此如果您发现缺少某些内容,请打开一个 issue 或 PR!

自定义一致性

您可以直接使用 plist 兼容的值

struct Convertible {
	var name: String
}

extension Convertible: DefaultsValueConvertible {
	typealias DefaultsRepresentation = String
	
	init(defaultsRepresentation: String) throws {
		name = defaultsRepresentation
	}
	
	func defaultsRepresentation() throws -> String {
		name
	}
}

或者将该操作委托给另一个符合 DefaultsValueConvertible 的类型。

struct IndirectConvertible {
	var name: String
}

extension IndirectConvertible: IndirectDefaultsValueConvertible {
	typealias DefaultsRepresentation = Convertible.DefaultsRepresentation
	
	init(indirectRepresentation: Convertible) throws {
		name = indirectRepresentation.name
	}
	
	func indirectRepresentation() throws -> Convertible {
		try .init(defaultsRepresentation: name)
	}
}

协议层次结构

plist 兼容的类型(如 BoolDataString 等)符合 DefaultsValue 协议,该协议建立了与 UserDefaults 之间的编码和解码。

@UserDefault 属性包装器适用于任何符合 DefaultsValueConvertible 的类型,该类型编码为符合 DefaultsValue 的关联类型,并从默认值中存储和加载。(DefaultsValue 本身也符合此协议,因此您可以将其与包装器一起使用。)

如果您希望委托给另一个 DefaultsValueConvertible 类型,而不是直接使用 plist 兼容类型,您可以改为符合 IndirectDefaultsValueConvertible(它继承自前者),允许您编码为任何 DefaultsValueConvertible 类型和从中解码。这意味着您可以拥有任意长度的编码/解码委托链,而无需编写太多样板代码。

如果您正在处理类似序列的类型,那么最好的选择可能是符合 ExpressibleByArrayExpressibleBySequence,这将允许您避免将元素映射到/从其默认表示形式的样板代码,前提是您可以从 Array 或任何 Sequence 初始化自己。