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

SwiftUI中如何通过UserDefaults正确解码已保存的Quests?

问题背景

开发一款任务管理应用,支持用户创建自定义Quests、赚取积分并兑换奖励。使用UserDefaults进行数据持久化时,奖励和积分的保存与加载均正常,但Quests在离开视图后会消失。调试显示编码成功,但解码失败。

已尝试方案
  • 确保Quests的保存逻辑与可正常工作的奖励保存逻辑一致
  • 使用打印语句确认编码成功并检查已保存数据
  • 验证UserDefaults中存在已保存的Quests数据
  • 尝试解码但持续收到错误
代码示例
import SwiftUI

class PointsManager: ObservableObject {
    @Published var quests: [Quest] = []
    
    init() {
        loadQuests()
    }

    func addQuest(title: String, totalPoints: Int) {
        let newQuest = Quest(title: title, totalPoints: totalPoints)
        quests.append(newQuest)
        saveQuests()
    }

    func saveQuests() {
        do {
            let encoded = try JSONEncoder().encode(quests)
            UserDefaults.standard.set(encoded, forKey: "quests")
            print("Encoding successful")
        } catch {
            print("Encoding failed: \(error.localizedDescription)")
        }
    }

    func loadQuests() {
        guard let savedData = UserDefaults.standard.data(forKey: "quests") else {
            print("No data found")
            return
        }
        do {
            quests = try JSONDecoder().decode([Quest].self, from: savedData)
            print("Decoding successful")
        } catch {
            print("Decoding failed: \(error.localizedDescription)")
        }
    }
}

struct Quest: Identifiable, Codable {
    var id = UUID()
    var title: String
    var totalPoints: Int
}
问题

为何编码成功但解码失败?如何在SwiftUI中通过UserDefaults正确加载并持久化Quests?


解决方案

解码失败的核心原因

最常见的触发场景是**UserDefaults中残留了不符合当前Quest结构的旧数据**:

  • 比如前期测试时存过错误格式的数据,或者修改过Quest的属性结构但未清理旧数据,导致JSONDecoder无法解析现有数据。
  • 虽然UUID原生支持Codable,但极少数情况下编码/解码时的格式不匹配也会引发问题,但概率极低。

具体修复步骤

1. 清理旧的缓存数据

调试阶段可以直接删除App的缓存(通过Xcode的"Product > Clean Build Folder",或卸载重装App),也可以临时在代码中添加清理逻辑(运行一次后删除):

// 仅调试用,运行一次后移除
UserDefaults.standard.removeObject(forKey: "quests")

2. 增强错误信息打印

原代码仅打印错误的本地化描述,无法定位具体问题,修改为打印完整错误详情:

// 在loadQuests的catch块中修改
print("Decoding failed: \(error)")

这样能看到具体的解码错误(比如哪个字段不匹配、类型错误等),快速定位问题。

3. 显式声明CodingKeys(可选但推荐)

虽然Quest结构默认符合Codable要求,但显式声明CodingKeys可以避免后续修改属性时出现意外的编码/解码问题:

struct Quest: Identifiable, Codable {
    var id = UUID()
    var title: String
    var totalPoints: Int
    
    enum CodingKeys: String, CodingKey {
        case id, title, totalPoints
    }
}

4. 优化加载失败后的处理逻辑

在解码失败时自动清理错误数据,避免下次启动仍加载失败:

func loadQuests() {
    guard let savedData = UserDefaults.standard.data(forKey: "quests") else {
        print("No data found")
        return
    }
    do {
        quests = try JSONDecoder().decode([Quest].self, from: savedData)
        print("Decoding successful")
    } catch {
        print("Decoding failed: \(error)")
        // 清理错误数据,重置quests
        UserDefaults.standard.removeObject(forKey: "quests")
        quests = []
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 14:52:26