SwiftUI中ObservableObject与Codable共存时游戏状态无法持久化
解决Wordle克隆游戏重启后状态丢失的问题
核心问题诊断
你的游戏重启后总是新建游戏,主要有两个关键原因:
- 可选类型解码错误:
toastText是String?类型,但你在init(from:)中用了decode而非decodeIfPresent,当toastText为nil时会直接导致解码失败,触发默认初始化流程。 - 默认初始化逻辑冲突:默认
init()中直接调用了newGame(),即使解码成功返回了保存的实例,首次启动时如果走默认初始化,会直接重置游戏状态。 - 缺少错误排查:
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
相关产品推荐
相关产品推荐

