持久化属性包装器

Swift Swift Version Cocoapods platforms GitHub

持久化属性包装器 是一个 Swift 库,旨在使在 Apple 平台上的 UserDefaults 数据库中持久化变量变得极其容易。

要使用 持久化属性包装器,你只需将一个变量注解为 @Persisted。它支持标准的 UserDefaults 类型(IntStringBoolDate 等),以及 RawRepresentable 枚举(其中 RawValue 可在 UserDefaults 中存储),以及任何符合 CodableNSSecureCoding 的类型。当然,也支持这些类型的任何 Optional 包装器。类型有效性在编译时进行检查:尝试在任何非支持类型的变量上使用将导致编译时错误。

用法

在你的变量上添加 @Persisted 属性。

初始化的第一个参数是字符串键,值将以此键存储在 UserDefaults 中。如果类型是非 Optional 的,你还必须提供一个 defaultValue,当 UserDefaults 中没有存储值时,将使用该默认值。

例如

@Persisted("UserSetting1", defaultValue: 42)
var someUserSetting: Int

@Persisted("UserSetting2") // defaultValue not necessary since Int? is an Optional type
var someOtherUserSetting: Int?

存储枚举

想要存储枚举值?如果枚举具有受支持在 UserDefaults 中存储的 backing type,那么这些枚举也可以标记为 @Persisted,并且存储在 UserDefaults 中的实际值将是枚举的原始值 (raw value)。例如

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 类型也可以被持久化;这会将变量的 JSON 编码表示形式存储在 UserDefaults 中。例如

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 片段 的值(例如 IntStringBool 等)将导致崩溃。这是由于在 iOS 13 之前发布的 Swift 运行时中的一个 错误。在这些情况下,使用 encodedDataKey 没有任何好处。

存储实现 NSCoding 的类型

任何符合 NSSecureCodingNSObject 也可以被持久化;这会将从 NSKeyedArchiver 获取的对象编码表示形式存储在 UserDefaults 中。例如

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 包装器上工作,问题变得更加复杂。一旦为我的应用程序解决了,我就想为什么不打包起来呢?

要求

安装

Swift Package Manager

在 Xcode 中添加 https://github.com/AndrewBennet/PersistedPropertyWrapper.git 作为 Swift Package Dependency。

CocoaPods

要通过 CocoaPods 安装,请将以下行添加到你的 Podfile

pod 'PersistedPropertyWrapper', '~> 2.0'

手动

Sources 目录的内容复制到你的项目中。

替代方案