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

SwiftUI中ObservableObject与Codable共存时游戏状态无法持久化

解决Wordle克隆游戏重启后状态丢失的问题

核心问题诊断

你的游戏重启后总是新建游戏,主要有两个关键原因:

  1. 可选类型解码错误:toastText是String?类型,但你在init(from:)中用了decode而非decodeIfPresent,当toastText为nil时会直接导致解码失败,触发默认初始化流程。
  2. 默认初始化逻辑冲突:默认init()中直接调用了newGame(),即使解码成功返回了保存的实例,首次启动时如果走默认初始化,会直接重置游戏状态。
  3. 缺少错误排查:loadData()中用try?吞掉了解码错误,无法定位解码失败的具体原因。

修复步骤

1. 修正可选类型的解码逻辑

将init(from:)中toastText的解码方式改为decodeIfPresent,适配可选值的编码场景:

required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)

    guesses = try container.decode([Guess].self, forKey: .guesses)
    incorrectAttempts = try container.decode([Int].self, forKey: .incorrectAttempts)
    
    // 用decodeIfPresent处理可选类型,避免nil时解码失败
    toastText = try container.decodeIfPresent(String.self, forKey: .toastText)
    showStats = try container.decode(Bool.self, forKey: .showStats)
    currentStat = try container.decode(WordleStatistic.self, forKey: .currentStat)
    
    keyColors = try container.decode([String : String].self, forKey: .keyColors)
    matchedLetters = try container.decode([String].self, forKey: .matchedLetters)
    misplacedLetters = try container.decode([String].self, forKey: .misplacedLetters)
    selectedWord = try container.decode(String.self, forKey: .selectedWord)
    currentWord = try container.decode(String.self, forKey: .currentWord)
    tryIndex = try container.decode(Int.self, forKey: .tryIndex)
    inPlay = try container.decode(Bool.self, forKey: .inPlay)
    gameOver = try container.decode(Bool.self, forKey: .gameOver)
}

2. 拆分初始化与游戏启动逻辑

把默认init()中的newGame()调用移除,改为手动触发的初始化方法,避免加载保存数据后被重置:

class WordleDataModel: ObservableObject, Codable{
    // ... 所有属性保持不变 ...

    // 默认初始化器,仅完成基础属性初始化,不启动新游戏
    init() {
        currentStat = WordleStatistic.loadStat()
    }

    // 首次初始化时调用,设置初始状态并启动新游戏
    func setupInitialState() {
        populateDefaults()
        newGame()
    }

    // ... 其他方法保持不变 ...
}

3. 增强loadData()的错误排查与逻辑

添加错误捕获并打印日志,明确区分“加载成功”“解码失败”“无保存数据”三种场景:

static func loadData() -> WordleDataModel {
    if let savedData = UserDefaults.standard.object(forKey: "WordleData") as? Data {
        do {
            let currentData = try JSONDecoder().decode(WordleDataModel.self, from: savedData)
            print("成功加载保存的游戏状态")
            return currentData
        } catch {
            print("解码失败,使用新游戏状态:\(error)")
            let model = WordleDataModel()
            model.setupInitialState()
            return model
        }
    } else {
        print("无保存数据,初始化新游戏")
        let model = WordleDataModel()
        model.setupInitialState()
        return model
    }
}

4. 确保自定义类型的Codable实现

确认Guess和WordleStatistic两个自定义类型完全遵循Codable协议,所有存储属性都支持编码解码——这是整个模型能成功序列化的前提。

额外优化建议

  • 自动触发保存:给需要持久化的属性添加didSet,状态变化时自动调用saveData(),比如:
    @Published var guesses: [Guess] = [] {
        didSet { saveData() }
    }
    @Published var currentWord = "" {
        didSet { saveData() }
    }
    
  • 覆盖所有状态变化:非@Published属性(如keyColors、matchedLetters)修改后,也要手动调用saveData(),避免状态遗漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:13:16