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

SwiftUI如何将自定义ObservedObject类数据保存到UserDefaults?

解决ObservedObject数据持久化到UserDefaults的问题

我完全懂你的困扰——自定义的ObservableObject数据一重启App就丢失,直接存UserDefaults还崩溃,这事儿确实头疼。核心原因是UserDefaults只能存储支持PropertyList的基础类型(比如String、Date、Number、Data这类),自定义类对象直接丢进去肯定不行,得先把它转成Data格式,这就需要让你的ProgressData遵守Codable协议来实现编码解码。

下面一步步帮你搞定:

1. 改造ProgressData类,添加持久化能力

首先给ProgressData加上Codable协议,然后实现保存到UserDefaults和从UserDefaults加载的方法。因为你用的属性(Date、Float)都是默认支持Codable的,所以大部分编码解码逻辑可以自动合成,只需补充少量代码:

import SwiftUI

class ProgressData: ObservableObject, Codable {
    // 定义需要编码/解码的属性对应的Key
    enum CodingKeys: String, CodingKey {
        case licenseDate, dayProgressValue, nightProgressValue, newDriverProgressValue
    }
    
    @Published var licenseDate: Date = Date()
    @Published var dayProgressValue: Float = 0.00
    @Published var nightProgressValue: Float = 0.00
    @Published var newDriverProgressValue: Float = 0.00
    
    // 默认初始化方法
    init() {}
    
    // Codable解码初始化(从Data转成对象)
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        licenseDate = try container.decode(Date.self, forKey: .licenseDate)
        dayProgressValue = try container.decode(Float.self, forKey: .dayProgressValue)
        nightProgressValue = try container.decode(Float.self, forKey: .nightProgressValue)
        newDriverProgressValue = try container.decode(Float.self, forKey: .newDriverProgressValue)
    }
    
    // Codable编码方法(把对象转成Data)
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(licenseDate, forKey: .licenseDate)
        try container.encode(dayProgressValue, forKey: .dayProgressValue)
        try container.encode(nightProgressValue, forKey: .nightProgressValue)
        try container.encode(newDriverProgressValue, forKey: .newDriverProgressValue)
    }
    
    // 保存到UserDefaults的方法
    func saveToUserDefaults() {
        do {
            let data = try JSONEncoder().encode(self)
            UserDefaults.standard.set(data, forKey: "SavedProgressData")
        } catch {
            print("保存失败:\(error.localizedDescription)")
        }
    }
    
    // 从UserDefaults加载的静态方法
    static func loadFromUserDefaults() -> ProgressData? {
        guard let savedData = UserDefaults.standard.data(forKey: "SavedProgressData") else {
            return nil
        }
        do {
            return try JSONDecoder().decode(ProgressData.self, from: savedData)
        } catch {
            print("加载失败:\(error.localizedDescription)")
            return nil
        }
    }
}

2. 在MainView启动时加载保存的数据

这样每次打开App,会先尝试读取之前保存的数据,没有的话再用默认初始化的新对象:

struct MainView: View {
    @ObservedObject var data: ProgressData
    
    init() {
        // 优先加载保存的数据,没有则用默认
        if let savedData = ProgressData.loadFromUserDefaults() {
            self.data = savedData
        } else {
            self.data = ProgressData()
        }
    }
    
    var body: some View {
        // 你的主界面代码,比如导航到设置页
        NavigationStack {
            VStack {
                // ... 你的界面元素
                NavigationLink("进入设置") {
                    SettingsView(data: data)
                }
            }
        }
    }
}

3. 在SettingsView按钮中触发保存

现在点击按钮时,直接调用我们写好的saveToUserDefaults()方法就行:

struct SettingsView: View {
    @ObservedObject var data: ProgressData
    
    var body: some View {
        Button(action: {
            // 点击按钮时保存数据
            data.saveToUserDefaults()
            // 可以加个提示,比如弹出Alert告诉用户保存成功
        }) {
            Text("保存当前进度")
                .padding()
                .background(Color.accentColor)
                .foregroundColor(.white)
                .cornerRadius(8)
        }
    }
}

额外优化小建议

  • 如果不想手动点按钮保存,可以用Combine监听@Published属性的变化,自动触发保存(比如在ProgressData初始化时添加监听),这样数据变化时就能自动持久化。
  • UserDefaults适合存储小体量数据,如果以后你的数据结构变复杂,建议改用Core Data或者Realm这类本地数据库。

内容的提问来源于stack exchange,提问作者Ariel Turjeman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 16:07:28