Foil
为 UserDefaults 实现的一个轻量级、正确的属性包装器
关于
阅读文章:关于编写UserDefaults Property Wrapper的更好方法
为什么叫 Foil?
Foil,正如“让我快速地将这个剩余的食物用 foil 插好并存储起来,以便我以后可以吃”的含义。🌯 😉
Foil:
名词
北美
一种非常薄、可塑性、易撕的铝箔,用于烹饪、包装、化妆品和绝缘。
用法
您可以使用 @FoilDefaultStorage
用于非可选值,以及 @FoilDefaultStorageOptional
用于可选值。不过,您不一定需要将所有用户默认存储在一个地方。任何类型上的任何属性都可以使用这个包装器。
final class AppSettings {
static let shared = AppSettings()
@FoilDefaultStorage(key: "flagEnabled")
var flagEnabled = true
@FoilDefaultStorage(key: "totalCount")
var totalCount = 0
@FoilDefaultStorageOptional(key: "timestamp")
var timestamp: Date?
}
// Usage
func userDidToggleSetting(_ sender: UISwitch) {
AppSettings.shared.flagEnabled = sender.isOn
}
还包含一个示例应用项目。
enum
键
使用 如果您更喜欢使用 enum
作为键,编写特定于您应用的扩展很容易。但这不是必须的。事实上,除非您有特定理由需要引用键,否则这完全是多余的。
enum AppSettingsKey: String, CaseIterable {
case flagEnabled
case totalCount
case timestamp
}
extension FoilDefaultStorage {
init(wrappedValue: T, _ key: AppSettingsKey) {
self.init(wrappedValue: wrappedValue, key: key.rawValue)
}
}
extension FoilDefaultStorageOptional {
init(_ key: AppSettingsKey) {
self.init(key: key.rawValue)
}
}
观察变化
有 许多方法可以观察属性的变化。最常见的方法是使用 Key-Value Observing (KVO) 或 Combine Publisher。使用 KVO 观察,需要对象继承自 NSObject
,并且属性必须声明为 @objc dynamic
。
final class AppSettings: NSObject {
static let shared = AppSettings()
@FoilDefaultStorageOptional(key: "userId")
@objc dynamic var userId: String?
@FoilDefaultStorageOptional(key: "average")
var average: Double?
}
使用 KVO
let observer = AppSettings.shared.observe(\.userId, options: [.new]) { settings, change in
print(change)
}
使用 Combine
注意
average
不需要 @objc dynamic
注解,.receiveValue
会立即发射当前 average
的值,并且在每次更改之后都会发射。
AppSettings.shared.$average
.sink {
print($0)
}
.store(in: &cancellable)
使用 KVO 的 Combine 替代方案
注意
在这种情况下,userId
需要编写 @objc dynamic
注解,而 AppSettings
需要继承自 NSObject
。然后,receiveValue
将仅在外部对象值更改时触发,它不会像上面示例那样发布初始值。
AppSettings.shared
.publisher(for: \.userId, options: [.new])
.sink {
print($0)
}
.store(in: &cancellable)
支持的类型
以下类型默认支持与 @FoilDefaultStorage
一起使用。
重要
通过遵循 UserDefaultsSerializable
可以从支持自定义类型,但是高度不建议这样做,因为默认情况下所有 plist
类型都得到支持。UserDefaults
不是一个存储复杂数据结构和对象图的设计。您可能应该使用一个合适的数据库(或其他通过 Codable
序列化到磁盘)。
虽然 Foil
默认支持存储 Codable
类型,但应谨慎使用,并且 仅限于 具有少量属性的轻量级对象。
Bool
Int
UInt
Float
Double
String
URL
Date
Data
Array
Set
Dictionary
RawRepresentable
类型Codable
类型
警告
如果您正在存储自定义的 Codable
类型,并使用由Foil
提供的默认的UserDefaultsSerializable
实现,那么您必须使用属性包装器的可选变体@FoilDefaultStorageOptional
。这样可以将您的 Codable
类型进行破坏性的变更(例如:添加或删除属性)。或者,您可以提供一个支持迁移的自定义 Codable
实现,或提供一个处理编码/解码错误的自定义UserDefaultsSerializable
实现。请参考下面的示例。
Codable示例
// Note: uses the default implementation of UserDefaultsSerializable
struct User: Codable, UserDefaultsSerializable {
let id: UUID
let name: String
}
// Yes, do this
@FoilDefaultStorageOptional(key: "user")
var user: User?
// NO, do NOT this
// This will crash if you change User by adding/removing properties
@FoilDefaultStorage(key: "user")
var user = User()
其他资源
支持的平台
- iOS 13.0+
- tvOS 13.0+
- watchOS 6.0+
- macOS 11+
需求
- Swift 5.9+
- Xcode 15.0+
- SwiftLint
安装
CocoaPods
pod 'Foil', '~> 5.0.0'
Swift Package Manager
dependencies: [
.package(url: "https://github.com/jessesquires/Foil.git", from: "5.0.0")
]
或者,您可以直接通过Xcode添加包。
文档
您可以在此处阅读文档。由jazzy生成。由GitHub Pages托管。
参与贡献
如果您想为此项目做出贡献,请查看以下指南。
致谢
由 Jesse Squires 创建和维护。
许可协议
基于MIT许可证发布。详情请参阅LICENSE
文件。
版权所有 © 2021-至今 Jesse Squires。