什么是 @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()
}
}
解释: #
@AppStorage("username")
:- 将
username
属性绑定到UserDefaults
的键"username"
,初始值为Guest
。
- 将
- 自动监听:
- 当
username
属性的值被用户在TextField
中修改时,它会自动同步到UserDefaults
,并触发Text
视图重新渲染。
- 当
@AppStorage 支持的数据类型 #
AppStorage
支持以下常见类型,与 UserDefaults
基本一致:
- 基本数据类型:
Int
,Double
,Float
,Bool
,String
- 集合类型(如
Data
默认支持,但需手动编码数据)。 - 枚举(原始值必须是支持的类型,例如
String
或Int
)。
@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 编码) #
如果你需要存储复杂类型(如自定义对象),可以将它们转化为 Data
或 String
。
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 的注意事项 #
不要存储大量数据:
@AppStorage
的底层依赖UserDefaults
,而UserDefaults
更适合存储轻量级的配置(如布尔值、字符串、数字等),不推荐用来存储大数据(如图片或文件)。
避免密集操作:
UserDefaults
不是高性能存储,每次写入/读取都需要一定的性能开销。如果需要频繁修改或大量存储,考虑使用 Core Data 或 SQLite。
跨设备同步 iCloud(可选):
- 如果你希望
AppStorage
值在多台设备间同步,可以将存储的值绑定到共享的UserDefaults
容器(如iCloud
):@AppStorage("username", store: UserDefaults(suiteName: "iCloud.com.example.myapp")) var username: String = "Guest"
- ⚠️ 确保正确设置 App 的 iCloud 容器。
- 如果你希望
安全类型转换:
- 如果
@AppStorage
获取不到值(比如数据被外部清除),它会返回默认值(如果设置了)。确保在实现时正确定义默认值,防止应用崩溃。
- 如果
和其他 SwiftUI 数据持久化方式的对比 #
1. 和 @SceneStorage
的区别
#
@SceneStorage
适用于每个不同 “场景”(即窗口)的独立状态存储,而 @AppStorage
是全局共享的(所有场景访问同一份数据)。
属性 | 特性 | 用途 |
---|---|---|
@AppStorage | 持久化到 UserDefaults | 用于跨场景的设置或全局状态 |
@SceneStorage | 恢复场景级别状态 | 适用于多窗口场景,各窗口状态独立 |
2. 和 UserDefaults
的区别
#
@AppStorage
是现代化的、类型安全的,和 SwiftUI 绑定。但是底层仍然依赖于 UserDefaults
。
操作类型 | UserDefaults 方法 | @AppStorage (更简洁) |
---|---|---|
存储用户偏好 | UserDefaults.standard.set() | 使用简单变量即可 |
监听和 UI 更新 | 需要手动更新 | 自动完成数据绑定和 UI 刷新 |
总结 #
AppStorage
简化了UserDefaults
的使用,同时自动绑定到 SwiftUI 的视图更新机制。- 应用场景:存储轻量和重要的配置项,比如应用主题、用户语言、通知开关等。
- 进阶用法:通过枚举、自定义类型等扩展其功能。
store 参数枚举 #
@AppStorage
的 store
参数用于指定数据存储的目标位置。它是一个可选的参数,默认使用的是 UserDefaults.standard
,即当前 App 的默认用户偏好存储。虽然 store
接收的是一个 UserDefaults
实例,但 SwiftUI 并未直接提供与 store
参数关联的具体枚举,而是让开发者基于不同的 UserDefaults
实例来选择适当的存储路径。
以下是一般情况下,你可以通过 store
参数传递的几种常见实例选项(与 UserDefaults
相关)。
常用的 UserDefaults
实例选项
#
1. 默认存储:UserDefaults.standard
#
- 这是
UserDefaults
默认的实例,表示数据存储在当前 App 的沙盒内。 - 如果未指定
store
参数,@AppStorage
会默认使用UserDefaults.standard
。
示例: #
@AppStorage("username", store: .standard) var username: String = "Guest"
等同于下面的简写(因为 store: .standard
是默认值):
#
@AppStorage("username") var username: String = "Guest"
当你不需要自定义存储路径,只希望以普通的沙盒方式存储用户偏好数据时,使用默认的 .standard
。
2. 共享存储:UserDefaults(suiteName:)
#
- 如果你的应用需要共享多个 App 或 App Extension 之间的数据,可以使用 App Groups 配置并提供
suiteName
。 store
参数可以指定一个共享的UserDefaults
实例,比如:UserDefaults(suiteName: "group.com.example.myAppGroup")
- 适用于跨 App 数据共享(例如 iOS 应用和其 Widget 之间)。
示例: #
@AppStorage("sharedKey", store: UserDefaults(suiteName: "group.com.example.myAppGroup"))
var sharedData: String = "Default Value"
关键点: #
suiteName
必须匹配你在 Xcode 中启用的App Group Identifier。- 多个 App 和 Extension 中使用相同的
suiteName
会共享数据。
3. 临时存储:自定义 UserDefaults
实例
#
你也可以创建一个完全独立的 UserDefaults
实例,用于临时存储一些调试、测试或特定的非全局数据。
示例: #
let customDefaults = UserDefaults(suiteName: "custom.testing.defaults")
@AppStorage("testingKey", store: customDefaults) var tempData: Int = 0
在这种情况下,数据会存储在专用的 UserDefaults
容器中,而不会影响 .standard
或 App Group 的存储。
store
参数的可选性
#
如果不传入 store
参数:
- 默认使用
UserDefaults.standard
。 - 如果你需要切换到共享 App Group 存储,则必须通过
UserDefaults(suiteName:)
显式提供。
默认写法:
@AppStorage("someKey") var value: String = "DefaultValue"
等价于指定 .standard
:
@AppStorage("someKey", store: .standard) var value: String = "DefaultValue"
其他特殊场景 #
- 子线程共享存储
如果你在多线程环境下需要共享一个特定的存储实例,可以手动传递相应的
UserDefaults
。 - 对特殊沙盒路径的数据存储
如果你的数据需要存储在自定义的存储位置,比如调试时隔离某些数据,你也可以通过
suiteName
提供沙盒路径。
总结:用户可用 store
参数表示的列表
#
选项 | 描述 | 示例 |
---|---|---|
.standard (默认值) | App 的默认 UserDefaults 存储,针对当前应用范围内保存用户偏好数据。 | @AppStorage(key, store: .standard) |
.suiteName("group.identifier") | 共享存储,允许多个 App 和扩展(Widget、App Extension 等)使用相同存储空间,需使用 App Groups 配置。 | @AppStorage(key, store: UserDefaults(suiteName: "group.yourAppGroupID")) |
自定义 UserDefaults 实例 | 用于创建单独的存储实例,可用于隔离测试、调试、或特定用例下的数据存储需求。 | UserDefaults(suiteName: "custom.storage") |
SwiftUI 本身并未提供封装的枚举列表,而是直接继承了 UserDefaults
的灵活性,因此枚举选项需要来自 UserDefaults
本身。
实践建议 #
- 如果没有特别需求,选择默认值
.standard
即可。 - 如果需要多个 App 或扩展间共享数据,请配置 App Groups,并传入符合
suiteName
的UserDefaults
实例。 - 如果在调试场景或对数据隔离有强需求时,可以使用自定义
UserDefaults
实例。
这个设计让 @AppStorage
的可用性变得更加广泛,同时仍保持了 SwiftUI 与 UIKit 数据管理的互操作性。