如何用SwiftData实现单实例数据实体(持久化应用设置)?
SwiftData 单例设置实体的持久化方案
核心思路
SwiftData 的@Query仅支持数组类型,我们可以通过封装一个ObservableObject管理器来处理单条设置实体的获取、初始化和全局共享,既符合SwiftData的规则,又能让设置在整个App中响应式更新。
步骤1:定义Setting实体
给Setting添加唯一标识字段,确保数据库中只会存在一条设置记录:
import SwiftData @Model class Setting { var id: UUID var activeItemId: UUID? // 存储上次活跃的实体ID // 其他需要持久化的设置字段,可按需扩展 var darkModeEnabled: Bool init(id: UUID = UUID(), activeItemId: UUID? = nil, darkModeEnabled: Bool = false) { self.id = id self.activeItemId = activeItemId self.darkModeEnabled = darkModeEnabled } }
步骤2:创建全局设置管理器
这个管理器作为ObservableObject,封装SwiftData的查询逻辑,自动处理“无设置时创建默认设置”的场景:
import SwiftUI import SwiftData class SettingManager: ObservableObject { @Query private var settings: [Setting] private let modelContext: ModelContext // 提供唯一的设置实例 var currentSetting: Setting { if let setting = settings.first { return setting } else { // 数据库中无设置时,创建默认设置并保存 let newSetting = Setting() modelContext.insert(newSetting) try? modelContext.save() return newSetting } } init(modelContext: ModelContext) { self.modelContext = modelContext // 查询限制仅取第一条,确保单例逻辑 _settings = Query(filter: #Predicate<Setting> { _ in true }, limit: 1) } // 更新活跃实体ID的方法 func updateActiveItemId(_ id: UUID?) { currentSetting.activeItemId = id try? modelContext.save() } // 切换深色模式的示例方法 func toggleDarkMode() { currentSetting.darkModeEnabled.toggle() try? modelContext.save() } }
步骤3:在App中注入管理器
修改App结构体,移除错误的@Binding,改为在环境中注入SettingManager:
import SwiftUI import SwiftData @main struct DataTestApp: App { let sharedModelContainer: ModelContainer = { let schema = Schema([ Item.self, Setting.self, ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } }() var body: some Scene { WindowGroup { ContentView() .environmentObject(SettingManager(modelContext: sharedModelContainer.mainContext)) } .modelContainer(sharedModelContainer) } }
步骤4:在视图中使用设置
在任意子视图中,通过@EnvironmentObject获取管理器,直接使用或更新设置:
struct ContentView: View { @EnvironmentObject var settingManager: SettingManager @Query var items: [Item] var body: some View { VStack { Text("当前活跃实体ID: \(settingManager.currentSetting.activeItemId?.uuidString ?? "无")") Toggle("深色模式", isOn: $settingManager.currentSetting.darkModeEnabled) .onChange(of: settingManager.currentSetting.darkModeEnabled) { _ in try? settingManager.modelContext.save() } // 示例:点击Item更新活跃ID List(items) { item in Button(item.name) { settingManager.updateActiveItemId(item.id) } } } } }
关键说明
- 管理器的作用:封装
@Query数组到单例的转换,同时通过ObservableObject让设置变化能被视图感知。 - 单例保证:通过查询限制1条,加上初始化时自动创建默认设置,确保数据库中永远只有一条Setting记录。
- 响应式更新:Setting是
@Model对象,修改属性时SwiftData会自动跟踪变化,结合ObservableObject,视图会自动刷新。
内容的提问来源于stack exchange,提问作者Virtual Yogi
相关产品推荐
相关产品推荐

