ObservableDefaults

Swift 6 License: MIT

ObservableDefaults 是一个 Swift 库,它将 UserDefaults 与 WWDC 2023 中引入的新的 SwiftUI Observation 框架集成在一起。它提供了一个宏 @ObservableDefaults,通过自动将声明的存储属性与 UserDefaults 键关联,从而简化了 UserDefaults 数据的管理。这使得能够精确而高效地响应 UserDefaults 的变化,无论这些变化来自应用程序内部还是外部。

动机

在 SwiftUI 中管理多个 UserDefaults 键可能会导致代码臃肿并增加出错的风险。虽然 @AppStorage 简化了单个 UserDefaults 键的处理,但它在处理多个键时无法很好地扩展,也无法提供精确的视图更新。随着 Observation 框架的引入,需要一种解决方案能够有效地将 UserDefaults 与 SwiftUI 的状态管理连接起来。

创建 ObservableDefaults 是为了应对这些挑战,提供一个全面而实用的解决方案。它利用宏来减少样板代码,并确保您的 SwiftUI 视图能够准确地响应 UserDefaults 中的更改。

有关 @AppStorage 的局限性以及 ObservableDefaults 背后的动机的深入讨论,您可以阅读 我的博客上的完整文章。


不要错过有关 Swift、SwiftUI、Core Data 和 SwiftData 的最新更新和精彩文章。订阅 Fatbobman's Swift Weekly,每周直接在您的收件箱中接收见解和有价值的内容。


特性

安装

您可以使用 Swift Package Manager 将 ObservableDefaults 添加到您的项目中

  1. 在 Xcode 中,转到 File > Add Packages...
  2. 输入存储库 URL:https://github.com/fatbobman/ObservableDefaults
  3. 选择包并将其添加到您的项目中。

用法

基本示例

导入 ObservableDefaults 后,您可以使用 @ObservableDefaults 注释您的类,以自动管理 UserDefaults 同步

import ObservableDefaults

@ObservableDefaults
class Settings {
    var name: String = "Fatbobman"
    var age: Int = 20
}
observableDefaults-1_2024-10-09_14.54.53-1.mp4

这个宏会自动

在 SwiftUI 视图中使用

您可以像这样在您的 SwiftUI 视图中使用 Settings

import SwiftUI

struct ContentView: View {
    @State var settings = Settings()

    var body: some View {
        VStack {
            Text("Name: \(settings.name)")
            TextField("Enter name", text: $settings.name)
        }
        .padding()
    }
}

使用额外的宏自定义行为

该库提供了额外的宏以进行更精细的控制

示例

@ObservableDefaults
public class Test1 {
    @DefaultsKey(userDefaultsKey: "firstName")
    // Automatically adds @DefaultsBacked
    public var name: String = "fat"

    // Automatically adds @DefaultsBacked
    public var age = 109

    // Only observes, not persisted in UserDefaults
    @ObservableOnly
    public var height = 190

    // Not observable and not persisted
    @Ignore
    public var weight = 10
}

在这个例子中

初始化器和参数

如果所有属性都有默认值,则可以使用自动生成的初始化器

public init(
    userDefaults: UserDefaults? = nil,
    ignoreExternalChanges: Bool? = nil,
    prefix: String? = nil
)

参数

用法示例

@State var settings = Settings(
    userDefaults: .standard,
    ignoreExternalChanges: false,
    prefix: "myApp_"
)

宏参数

您还可以直接在 @ObservableDefaults 宏中设置参数

示例

@ObservableDefaults(autoInit: false, ignoreExternalChanges: true, prefix: "myApp_")
class Settings {
    @DefaultsKey(userDefaultsKey: "fullName")
    var name: String = "Fatbobman"
}

自定义初始化器

如果将 autoInit 设置为 false,则需要创建自己的初始化器并显式开始监听 UserDefaults 更改

init() {
    // Start listening for changes
    observerStarter()
}

与其他可观察对象集成

建议将 UserDefaults 数据与您的主应用程序状态分开管理

@Observable
class ViewState {
    var selection = 10
    var isLogin = false
    let settings = Settings()
}

struct ContentView: View {
    @State var state = ViewState()

    var body: some View {
        VStack(spacing: 30) {
            Text("Name: \(state.settings.name)")
            Button("Modify Instance Property") {
                state.settings.name = "User \(Int.random(in: 0...1000))"
            }
            Button("Modify UserDefaults Directly") {
                UserDefaults.standard.set("User \(Int.random(in: 0...1000))", forKey: "name")
            }
        }
        .buttonStyle(.bordered)
    }
}

Observe First 模式

您可以通过在 @ObservableDefaults 宏中设置 observeFirst 参数来启用此模式

@ObservableDefaults(observeFirst: true)

启用此模式后,只有显式标记为 @DefaultsBacked 的属性才会持久保存到 UserDefaults。所有其他属性将自动应用 @ObservableOnly 宏,使其可观察但不会持久保存。可以将其视为标准模式的逆向模式,专注于可观察性,同时根据需要向单个属性添加持久性功能。

// Observe First Mode
@ObservableDefaults(observeFirst: true)
public class Test2 {
    // Automatically adds @ObservabeOnly
    public var name: String = "fat"

    // Automatically adds @ObservabeOnly
    public var age = 109

    // In Observe First Mode, only properties that need to be persisted require the use of @DefaultsBacked for annotation, and userDefaultsKey can be set within it
    @DefaultsBacked(userDefaultsKey: "myHeight")
    public var height = 190

    // Not observable and not persisted
    @Ignore
    public var weight = 10
}

重要提示

许可证

ObservableDefaults 在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE


鸣谢

特别感谢 Swift 社区的持续支持和贡献。

Buy Me A Coffee