SwiftUI中如何想要管理Xcode预览中的UserDefaults实例,或者想要实现应用见共享,则需要用到隔离的UserDefaults实例。
@AppStorage("yourKey", store: UserDefaults(suiteName: "PreviewUserDefaults")) var yourValue: String = ""
代码解析
1、@AppStorage
@AppStorage 是 SwiftUI 提供的属性包装器,用于将视图中的属性与 UserDefaults 中的值绑定。它可以让 SwiftUI 自动监听值的变化并更新 UI。
“yourKey”: 这是存储在 UserDefaults 中的键名,用来唯一标识该数据。
store 参数: 指定自定义的 UserDefaults 实例(默认使用 UserDefaults.standard)。
var yourValue: String = “”: 定义了绑定到 UserDefaults 的 SwiftUI 属性,并提供默认值(这里是空字符串 “”)。
2、UserDefaults(suiteName: “PreviewUserDefaults”)
创建了一个 UserDefaults 的自定义实例,使用指定的 App Group 或 Suite 名称 PreviewUserDefaults。
用途: 当希望在不同的上下文(如 Xcode 预览、App Group 共享存储)中使用隔离的 UserDefaults 存储,而不是影响全局的 UserDefaults.standard。
![](https://fangjunyu.com/wp-content/uploads/2025/01/1-38.png)
yourKey 存储到一个 独立的 UserDefaults 分组(suiteName 为 PreviewUserDefaults)中。具体行为如下:
1、创建独立的 UserDefaults 分组:
UserDefaults(suiteName: “PreviewUserDefaults”) 创建了一个独立的存储分组,与默认的 UserDefaults.standard 分开。
该分组的数据是隔离的,只有通过 suiteName 为 PreviewUserDefaults 的 UserDefaults 实例才能访问。
2、将 yourKey 存储到该分组中:
@AppStorage 自动将与 yourKey 相关的数据存入该指定分组,而不是默认的全局存储(UserDefaults.standard)。
存储的数据可以通过以下方式直接查看:
let customStore = UserDefaults(suiteName: "PreviewUserDefaults")
let value = customStore?.string(forKey: "yourKey") // 访问隔离存储
3、分组标记行为:
可以理解为 yourKey 是绑定到 PreviewUserDefaults 分组的,而不是全局的 UserDefaults.standard。
因此:
如果直接访问 UserDefaults.standard 的 yourKey,是无法读取到数据的。
只有访问 UserDefaults(suiteName: “PreviewUserDefaults”) 的 yourKey,才能获取对应的值。
隔离的UserDefaults实例的作用
隔离的 UserDefaults 实例的作用,是为了在特定场景中存储和读取数据,而不影响全局的 UserDefaults.standard。它在同一设备上隔离不同的存储环境。
默认情况下,@AppStorage 使用的是 UserDefaults.standard,它是一个全局的存储。在不同的环境(例如调试、测试或多用户共享数据)中操作全局存储,很容易出现以下问题:
污染真实数据:在开发过程中测试或调试可能会修改用户的实际数据。
影响其他功能:一个场景中的数据操作可能会意外干扰其他场景的逻辑。
多环境需求:有些场景需要临时存储,或者希望为测试提供虚拟的数据,而不是直接操作实际的数据。
解决方法:
通过指定 suiteName(如 “PreviewUserDefaults”),可以创建一个隔离的 UserDefaults 实例,用于测试或其他特定用途,而不会影响全局的 UserDefaults.standard。
使用场景
1、隔离测试环境
在 Xcode 预览 或开发阶段,隔离的 UserDefaults 可用于安全测试:
修改数据只会影响隔离存储,不会影响真实的用户数据。
提供了一个虚拟存储环境,在预览中模拟 App 的行为。
2、多用户或多实例支持
在支持 多用户 或 多账户切换 的场景中,每个用户可以拥有独立的 UserDefaults 存储。比如:
为每个用户分配一个独立的存储实例。
不同的应用模块或组件可以使用自己的存储实例,避免数据冲突。
3、应用间共享(App Group)
如果有多个应用(或应用和扩展),希望共享某些数据,就可以通过设置相同的 suiteName 来实现跨进程共享。
示例代码
1、在预览中使用自定义 UserDefaults
struct ContentView: View {
@AppStorage("previewKey", store: UserDefaults(suiteName: "PreviewUserDefaults")) var previewValue: String = ""
var body: some View {
VStack {
Text("Current value: \(previewValue)")
Button("Change Value") {
previewValue = "Updated Value"
}
}
}
}
#Preview {
ContentView()
}
效果: previewValue 的修改只会影响 PreviewUserDefaults,不会污染全局的 UserDefaults.standard。
2、在实际应用中使用 App Group
struct SharedContentView: View {
@AppStorage("sharedKey", store: UserDefaults(suiteName: "com.example.MyAppGroup")) var sharedValue: String = ""
var body: some View {
VStack {
Text("Shared value: \(sharedValue)")
Button("Update Value") {
sharedValue = "Shared Across Apps"
}
}
}
}
效果: sharedValue 的数据可以在使用同一个 App Group 的多个应用或扩展中共享。
3、支持多用户的隔离存储
假设有一个支持多用户登录的应用,可以为每个用户创建独立的存储实例。
struct MultiUserView: View {
let currentUser: String
@AppStorage("userPreference", store: UserDefaults(suiteName: "User_\(UUID().uuidString)")) var userPreference: String = ""
var body: some View {
VStack {
Text("User \(currentUser) preference: \(userPreference)")
Button("Update Preference") {
userPreference = "Updated for \(currentUser)"
}
}
}
}
效果:每个用户的存储互不干扰,适用于需要隔离数据的多用户场景。
如何清除隔离存储的内容?
可以通过以下方法清除指定 suiteName 的隔离存储内容:
1、直接调用 removePersistentDomain
使用 UserDefaults 的 removePersistentDomain(forName:) 方法,清理特定 suiteName 的数据:
if let userDefaults = UserDefaults(suiteName: "PreviewUserDefaults") {
userDefaults.removePersistentDomain(forName: "PreviewUserDefaults")
userDefaults.synchronize() // 确保立即生效
}
解释:
removePersistentDomain(forName:) 会清除指定 suiteName 下的所有内容。
synchronize() 确保数据更改立即保存到磁盘。
2、在 Xcode 预览中自动清理
如果只是在预览环境中使用隔离存储,可以在 #Preview 或视图加载时自动清除:
@AppStorage("previewKey", store: UserDefaults(suiteName: "PreviewUserDefaults")) var previewValue: String = "Default"
#Preview {
// 在每次预览加载时清理存储
if let previewDefaults = UserDefaults(suiteName: "PreviewUserDefaults") {
previewDefaults.removePersistentDomain(forName: "PreviewUserDefaults")
}
return ContentView()
}
3、手动清理特定的 key
如果只想清理某个 key,而不是整个存储,可以单独移除该 key:
if let userDefaults = UserDefaults(suiteName: "PreviewUserDefaults") {
userDefaults.removeObject(forKey: "yourKey")
userDefaults.synchronize()
}
总结
1、隔离存储的意义:通过指定 suiteName,隔离存储数据,避免干扰默认的 UserDefaults.standard。
2、全局影响:只要指定了 suiteName,就不会影响默认存储,作用范围仅限于指定的隔离存储空间。
3、如何清理:通过 removePersistentDomain(forName:) 清除指定存储,或移除特定 key。