Persisted Property Wrapper
Persisted Property Wrapper 是一个 Swift 库,它可以在 Apple 平台上使变量在 UserDefaults
数据库中持久化的过程变得极为简单。
要使用 Persisted Property Wrapper,您只需将变量标记为 @Persisted
。它支持标准的 UserDefaults
类型(Int
、String
、Bool
、Date
等),以及 RawRepresentable
枚举,其中 RawValue
可存储在 UserDefaults
中,以及任何 Codable
类型。此外,当然还包括这些类型的任何 Optional
包装器。类型有效性将在编译时检查:试图在非支持类型的任何变量上使用它将导致编译时错误。
使用方法
将 @Persisted
属性应用于您的变量。
初始化器的第一个参数是用于在 UserDefaults
中存储值的字符串键。如果类型非可选,您还必须提供 defaultValue
,它将在 UserDefaults
没有值存储时使用。
例如
@Persisted("UserSetting1", defaultValue: 42)
var someUserSetting: Int
@Persisted("UserSetting2") // defaultValue not necessary since Int? is an Optional type
var someOtherUserSetting: Int?
存储枚举
想要存储枚举值吗?如果枚举有一个支持在 UserDefaults
中存储的底层类型,那么这些也可以标记为 @Persisted
,并存储在 UserDefaults
中的实际值将是枚举的原始值。例如
enum AppTheme: Int {
case brightRed
case vibrantOrange
case plainBlue
}
struct ThemeSettings {
// Stores the underlying integer backing the selected AppTheme
@Persisted("theme", defaultValue: .plainBlue)
var selectedTheme: AppTheme
}
存储 Codable 类型
任何 Codable 类型也可以存储;这将在 UserDefaults 中存储变量的 JSON 编码表示形式。例如
struct AppSettings: Codable {
var welcomeMessage = "Hello world!"
var isSpecialModeEnabled = false
var launchCount = 0
@Persisted(encodedDataKey: "appSettings", defaultValue: .init())
static var current: AppSettings
}
// Example usage: this will update the value of the stored AppSettings
func appDidLaunch() {
AppSettings.current.launchCount += 1
}
请注意,必须使用 encodedDataKey
参数标签。这是因为 UserDefaults
存储的类型也可以是 Codable
,因此需要消除歧义,了解使用了哪种存储方式。
例如,以下两个变量通过不同的机制进行存储
// Stores the integer in UserDefaults
@Persisted("storedAsInteger", defaultValue: 10)
var storedAsInteger: Int
// Store the data of a JSON-encoded representation of the value. Don't use on iOS 12!
@Persisted(encodedDataKey: "storedAsData", defaultValue: 10)
var storedAsData: Int
注意:在 iOS 12 中,使用 encodedDataKey
初始化器并将值编码为 JSON 片段(例如 Int
、String
、Bool
等)会导致崩溃。这是由 iOS 13 之前分发的 Swift 运行时中存在的 一个错误。在这些情况下,使用 encodedDataKey
并没有优势。
NSCoding
的类型
存储遵循 任何遵循 NSSecureCoding
的 NSObject
也可以存储;这将在 UserDefaults 中存储 NSKeyedArchiver
获取的对象的编码表示形式。例如
class CloudKitSyncManager {
@Persisted(archivedDataKey: "ckServerChangeToken")
var changeToken: CKServerChangeToken?
}
请注意,必须使用 archivedDataKey
参数标签。如前所述,这也是消除使用哪种存储方式歧义的必要条件。
注意:此存储机制仅支持 iOS 11 及以上。
替代存储
默认情况下,@Persisted
属性存储在 UserDefaults.standard
数据库中;要将值存储在不同的位置,请向属性包装器传递 storage:
参数
extension UserDefaults {
static var alternative = UserDefaults(suiteName: "alternative")!
}
@Persisted("alternativeStoredValue", storage: .alternative)
var alternativeStoredValue: Int?
为什么需要一个库?
毕竟,互联网上有许多类似工具的例子。例如,John Sundell的这篇帖子展示了如何用几行代码编写@UserDefaultsBacked属性包装器。
然而,在我开发我的应用的过程中,我发现自己真的想在UserDefaults中存储枚举值。对于任何由整数或字符串支持的枚举,都有一个明显理想的实现方式——存储枚举的原始值。为了提供一个单一的API来持久化既支持UserDefaults类型又支持由UserDefaults支持的类型背后的枚举值,这有点棘手;再加上还需要让所有这些也同样能在任何支持类型的Optional包装器上工作,问题变得更加复杂。一旦解决了我的应用中的问题,我就想,为什么不打包一下呢?
需求
- Xcode 12
- Swift 5.3
安装
Swift包管理器
在Xcode中将https://github.com/AndrewBennet/PersistedPropertyWrapper.git
添加为Swift包依赖项。
CocoaPods
要通过CocoaPods安装,请在Podfile中添加以下行:
pod 'PersistedPropertyWrapper', '~> 2.0'
手动
将Sources
目录的内容复制到你的项目中。
备选方案
- SwiftyUserDefaults 拥有更多功能,但您必须在一个特定的扩展中定义您存储的属性。
- AppStorage:原生 Apple 属性包装器,针对(且在)SwiftUI 定制,仅在 iOS 14 中可用