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

SwiftUI+SwiftData重复添加删除食谱崩溃问题求助

问题分析

报错核心是SwiftData期望ingredients属性为NSData类型,但实际传入的是Swift数组。这是因为SwiftData默认会将非模型对象的数组尝试归档为二进制数据,但你的Ingredient要么是未正确支持归档的结构体,要么改成@Model类后关联配置错误。

解决方案

1. 将Ingredient定义为@Model类,建立与Recipe的关联

把原本的Ingredient结构体改成@Model类,同时在Recipe的ingredients属性上添加@Relationship,明确父子关系和删除规则:

@Model
class Ingredient {
    var text: String
    var food: String
    var foodId: String
    
    init(text: String, food: String, foodId: String) {
        self.text = text
        self.food = food
        self.foodId = foodId
    }
}

@Model
class Recipe {
    var title: String
    var imageUrl: String
    @Relationship(deleteRule: .cascade) // 删除Recipe时自动删除关联的Ingredient
    var ingredients: [Ingredient]
    var url: String
    var totalTime: Double
    
    init(title: String, imageUrl: String, ingredients: [Ingredient], url: String, totalTime: Double) {
        self.title = title
        self.imageUrl = imageUrl
        self.ingredients = ingredients
        self.url = url
        self.totalTime = totalTime
    }
}

2. 修正API数据到模型的转换逻辑

API返回的是结构体类型的Ingredient,需要转换成@Model标记的Ingredient类实例,再传入Recipe初始化:

// 重命名API解码用的结构体,避免和模型类冲突
struct ApiIngredient: Decodable {
    var text: String
    var food: String
    var foodId: String
}

struct ApiResponse: Decodable {
    var count: Int
    var hits: [RecipeObject]
    
    struct RecipeObject: Decodable {
        var recipe: RecipeModel
        
        struct RecipeModel: Decodable {
            var label: String
            var image: String
            var ingredients: [ApiIngredient] // 使用重命名后的结构体
            var url: String
            var totalTime: Double
        }
    }
}

extension Recipe {
    convenience init(from recipe: ApiResponse.RecipeObject.RecipeModel) {
        // 将API结构体转换成模型类实例
        let ingredientModels = recipe.ingredients.map { apiIngredient in
            Ingredient(text: apiIngredient.text, food: apiIngredient.food, foodId: apiIngredient.foodId)
        }
        self.init(
            title: recipe.label,
            imageUrl: recipe.image,
            ingredients: ingredientModels,
            url: recipe.url,
            totalTime: recipe.totalTime
        )
    }
}

3. 确保上下文操作正确,手动保存状态

在添加/删除收藏的方法中,操作后手动保存上下文,避免状态不一致:

func addRecipeToFavorite(modelContext: ModelContext, recipe: Recipe) {
    modelContext.insert(recipe)
    do {
        try modelContext.save()
    } catch {
        print("保存收藏失败: \(error.localizedDescription)")
    }
}

func removeRecipeFromFavorite(modelContext: ModelContext, recipe: Recipe) {
    modelContext.delete(recipe)
    do {
        try modelContext.save()
    } catch {
        print("删除收藏保存失败: \(error.localizedDescription)")
    }
}

4. 删除后再次添加的注意事项

删除Recipe后,原实例已被标记为删除状态,不能再次插入。再次添加时,必须基于API数据创建新的Recipe实例,不能复用之前被删除的对象。

关键说明
  • SwiftData中,关联的模型必须都是@Model标记的类,不能混合结构体和模型类数组
  • @Relationship的deleteRule: .cascade确保删除Recipe时,关联的Ingredient会被自动删除,避免数据残留
  • 手动保存上下文可以确保操作立即持久化,避免上下文状态混乱导致崩溃

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 17:23:12