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

SwiftUI将数据存入UserDefaults后列表视图读取为nil如何解决

问题排查及修复方案

1. 首先确认Meal模型已遵守Codable协议

你当前使用JSONEncoder/JSONDecoder处理序列化,要求Meal以及它所有属性的类型都必须实现Codable协议,漏写的话会导致编码直接失败,try?会返回nil,数据根本不会写入UserDefaults,读取自然为空。
示例正确的模型声明:

struct Meal: Identifiable, Codable { // 必须声明遵守Codable协议
    let id = UUID()
    let name: String
    let imageUrlString: String
    let ingredients: [String]
    let instructions: String
    let area: String
    let category: String
}

如果你的模型里包含自定义类型,该自定义类型也需要同步遵守Codable协议,否则也会导致编码失败。

2. 确认全局使用同一个SavedMeals实例

如果在不同页面各自初始化SavedMeals(),会导致你在ContentView里修改的实例和SavedMealsView接收的不是同一个对象,读取自然是空。
正确的做法是在App入口处初始化一次,通过@EnvironmentObject全局传递:

@main
struct YourApp: App {
    @StateObject private var savedMeals = SavedMeals()
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(savedMeals)
        }
    }
}

然后在所有用到的页面通过环境变量读取,不要单独初始化:

// ContentView和SavedMealsView里统一这么声明
@EnvironmentObject var savedMeals: SavedMeals

3. 移除强制解包避免逻辑中断

你代码里的self.mealGenerator.currentMeal!强制解包如果遇到currentMeal为nil的情况会直接崩溃,不会走到后续的存数据逻辑,建议改成可选绑定判断:

Button("Save Meal") {
    guard let currentMeal = self.mealGenerator.currentMeal else { return }
    let meal = Meal(name: currentMeal.name,
                    imageUrlString: currentMeal.imageUrlString,
                    ingredients: currentMeal.ingredients,
                    instructions: currentMeal.instructions,
                    area: currentMeal.area,
                    category: currentMeal.category)
    self.savedMeals.meals.append(meal)
    self.savedMeals.saveMeals()
}

4. 调试定位具体失败节点

可以加打印快速定位是存失败还是读失败:

// 在saveMeals里添加错误打印
func saveMeals() {
    do {
        let encoded = try JSONEncoder().encode(meals)
        UserDefaults.standard.set(encoded, forKey: "Meals")
        print("保存成功,数据长度:\(encoded.count)")
    } catch {
        print("编码失败:\(error)") // 这里会输出具体的编码错误原因
    }
}

// 在init里添加错误打印
init() {
    if let mealsData = UserDefaults.standard.data(forKey: "Meals") {
        print("读取到存储数据,长度:\(mealsData.count)")
        do {
            let decoded = try JSONDecoder().decode([Meal].self, from: mealsData)
            self.meals = decoded
            print("解码成功,共\(decoded.count)条数据")
            return
        } catch {
            print("解码失败:\(error)") // 输出具体的解码错误原因
        }
    }
    self.meals = []
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 12:15:10