You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:55:17