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 类型,但应谨慎使用,并且 仅限于 具有少量属性的轻量级对象。
BoolIntUIntFloatDoubleStringURLDateDataArraySetDictionaryRawRepresentable类型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。