SwiftUI — AppStorage

什么是 @AppStorage #

SwiftUI 中,@AppStorage 是一个属性包装器,用于便捷地访问 UserDefaults 中存储的数据。它提供了一种现代化和类型安全的方式来读取和存储轻量级的键值对数据,尤其适用于 持久化跨应用会话的简单设定(如用户偏好、配置信息等)。

当数据发生变化时,@AppStorage 会自动通知视图刷新,从而实现数据绑定和 UI 更新。


基本用法 #

使用 @AppStorage 表示一个绑定到 UserDefaults 的属性,你只需要提供一个键名。

示例 1:静态示例 #

import SwiftUI

struct ContentView: View {
    @AppStorage("username") var username: String = "Guest" // 绑定 UserDefaults 中的 "username"

    var body: some View {
        VStack {
            Text("Hello, \(username)") // 绑定的值改变时,视图会自动刷新
            TextField("Enter your name", text: $username) // 修改绑定值
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
        }
        .padding()
    }
}

解释: #

  1. @AppStorage("username")
    • username 属性绑定到 UserDefaults 的键 "username",初始值为 Guest
  2. 自动监听
    • username 属性的值被用户在 TextField 中修改时,它会自动同步到 UserDefaults,并触发 Text 视图重新渲染。

@AppStorage 支持的数据类型 #

AppStorage 支持以下常见类型,与 UserDefaults 基本一致:

  • 基本数据类型Int, Double, Float, Bool, String
  • 集合类型(如 Data 默认支持,但需手动编码数据)。
  • 枚举(原始值必须是支持的类型,例如 StringInt)。

@AppStorage 使用案例 #

示例 2:存储布尔值(应用主题) #

import SwiftUI

struct ContentView: View {
    @AppStorage("isDarkMode") var isDarkMode: Bool = false // 是否启用暗模式

    var body: some View {
        VStack {
            Text("Current Theme: \(isDarkMode ? "Dark" : "Light")")
                .padding()
            
            Toggle("Enable Dark Mode", isOn: $isDarkMode)
                .padding()
        }
        .preferredColorScheme(isDarkMode ? .dark : .light) // 根据用户偏好设置界面主题
    }
}

解释:

  • isDarkMode 绑定到 UserDefaults 中的 "isDarkMode" 键。
  • 切换开关时,UserDefaults 的值会更新,并自动切换应用主题。

@AppStorage 设置自定义数据类型 #

虽然 @AppStorage 默认支持基础数据类型,但对于一些复杂数据(如自定义对象或枚举),你需要做额外的处理。例如,利用枚举的原始值(RawValue),或将自定义类型序列化为可以存储的格式(如 JSON)。


示例 3:存储枚举值(原始值) #

import SwiftUI

enum AppTheme: String { // 定义枚举
    case light, dark
}

struct ContentView: View {
    @AppStorage("selectedTheme") var selectedThemeRaw: String = AppTheme.light.rawValue // 存储枚举原始值

    var selectedTheme: AppTheme {
        get { AppTheme(rawValue: selectedThemeRaw) ?? .light }
        set { selectedThemeRaw = newValue.rawValue }
    }

    var body: some View {
        VStack {
            Text("Selected Theme: \(selectedTheme == .dark ? "Dark" : "Light")")
            Button("Switch Theme") {
                selectedTheme = (selectedTheme == .light) ? .dark : .light
            }
        }
        .preferredColorScheme(selectedTheme == .dark ? .dark : .light)
    }
}

解释:

  • selectedThemeRaw 存储枚举的原始值(String 类型)。
  • 提供了 selectedTheme 属性用于在 UI 和枚举数据之间转换。

示例 4:存储复杂对象(使用 JSON 编码) #

如果你需要存储复杂类型(如自定义对象),可以将它们转化为 DataString

import SwiftUI

struct User: Codable {
    var name: String
    var age: Int
}

// 自定义 User 类型的 AppStorage
@propertyWrapper
struct AppStorageCodable<T: Codable>: DynamicProperty {
    let key: String
    let defaultValue: T

    @AppStorage private var storedData: Data?

    var wrappedValue: T {
        get {
            if let data = storedData {
                return try! JSONDecoder().decode(T.self, from: data)
            }
            return defaultValue
        }
        set {
            storedData = try? JSONEncoder().encode(newValue)
        }
    }
}

struct ContentView: View {
    @AppStorageCodable("currentUser", defaultValue: User(name: "Guest", age: 0)) var currentUser

    var body: some View {
        VStack {
            Text("Name: \(currentUser.name)")
            Text("Age: \(currentUser.age)")

            Button("Update User") {
                currentUser = User(name: "John", age: 35) // 更新对象
            }
        }
    }
}

解释:

  • AppStorageCodable 是一个自定义的属性包装器,用于将 Codable 类型转化为 AppStorage 支持的 Data 格式,并存储到 UserDefaults
  • 自动对复杂数据(如 User 对象)进行序列化和反序列化。

@AppStorage 的注意事项 #

  1. 不要存储大量数据

    • @AppStorage 的底层依赖 UserDefaults,而 UserDefaults 更适合存储轻量级的配置(如布尔值、字符串、数字等),不推荐用来存储大数据(如图片或文件)。
  2. 避免密集操作

    • UserDefaults 不是高性能存储,每次写入/读取都需要一定的性能开销。如果需要频繁修改或大量存储,考虑使用 Core DataSQLite
  3. 跨设备同步 iCloud(可选)

    • 如果你希望 AppStorage 值在多台设备间同步,可以将存储的值绑定到共享的 UserDefaults 容器(如 iCloud):
      @AppStorage("username", store: UserDefaults(suiteName: "iCloud.com.example.myapp")) var username: String = "Guest"
      
    • ⚠️ 确保正确设置 App 的 iCloud 容器。
  4. 安全类型转换

    • 如果 @AppStorage 获取不到值(比如数据被外部清除),它会返回默认值(如果设置了)。确保在实现时正确定义默认值,防止应用崩溃。

和其他 SwiftUI 数据持久化方式的对比 #

1. 和 @SceneStorage 的区别 #

@SceneStorage 适用于每个不同 “场景”(即窗口)的独立状态存储,而 @AppStorage 是全局共享的(所有场景访问同一份数据)。

属性特性用途
@AppStorage持久化到 UserDefaults用于跨场景的设置或全局状态
@SceneStorage恢复场景级别状态适用于多窗口场景,各窗口状态独立

2. 和 UserDefaults 的区别 #

@AppStorage 是现代化的、类型安全的,和 SwiftUI 绑定。但是底层仍然依赖于 UserDefaults

操作类型UserDefaults 方法@AppStorage(更简洁)
存储用户偏好UserDefaults.standard.set()使用简单变量即可
监听和 UI 更新需要手动更新自动完成数据绑定和 UI 刷新

总结 #

  • AppStorage 简化了 UserDefaults 的使用,同时自动绑定到 SwiftUI 的视图更新机制。
  • 应用场景:存储轻量和重要的配置项,比如应用主题、用户语言、通知开关等。
  • 进阶用法:通过枚举、自定义类型等扩展其功能。
本文共 1719 字,上次修改于 Jan 8, 2025