PersistedPropertyWrapper 2.1.0

PersistedPropertyWrapper 2.1.0

Andrew Bennet 维护。



  • 作者
  • Andrew Bennet

Persisted Property Wrapper

Swift Swift Version Cocoapods platforms GitHub

Persisted Property Wrapper 是一个 Swift 库,它可以在 Apple 平台上使变量在 UserDefaults 数据库中持久化的过程变得极为简单。

要使用 Persisted Property Wrapper,您只需将变量标记为 @Persisted。它支持标准的 UserDefaults 类型(IntStringBoolDate 等),以及 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 片段(例如 IntStringBool 等)会导致崩溃。这是由 iOS 13 之前分发的 Swift 运行时中存在的 一个错误。在这些情况下,使用 encodedDataKey 并没有优势。

存储遵循 NSCoding 的类型

任何遵循 NSSecureCodingNSObject 也可以存储;这将在 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 中可用