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

SwiftData应用关闭后数据关联丢失问题求助

SwiftData关联重启后丢失的解决方法

问题根源

你遇到的关联丢失问题,核心是双向关联未正确建立:

  • 虽然你将Recipe的method/ingredients数组赋值了解码后的子对象,但子对象(Method/Ingredient)的反向关联属性(recipe)仍为nil
  • SwiftData需要双向关联都被正确设置,才能在持久化时保存关联关系;仅设置父对象的数组,子对象的反向关联缺失会导致重启后关联断裂

具体修复步骤

1. 补全子对象的@Relationship注解

在Method类中,给recipe属性加上正确的反向关联注解(之前的代码被注释掉了,这是关键遗漏):

@Model
final class Method: Codable {
    enum CodingKeys: CodingKey {
        case order, step, stepComplete
    }

    var order: Int
    var step: String
    var stepComplete: Bool = false

    // 补全反向关联注解,指定关联到Recipe的method属性
    @Relationship(inverse: \Recipe.method)
    var recipe: Recipe?
    
    // Codable实现:关联字段无需解码/编码
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        order = try container.decode(Int.self, forKey: .order)
        step = try container.decode(String.self, forKey: .step)
        stepComplete = try container.decodeIfPresent(Bool.self, forKey: .stepComplete) ?? false
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(order, forKey: .order)
        try container.encode(step, forKey: .step)
        try container.encode(stepComplete, forKey: .stepComplete)
    }
}

Ingredient类也需要做同样修改,补全@Relationship(inverse: \Recipe.ingredients)注解。

2. 解码后手动建立双向关联

在loadData方法中,插入Recipe到上下文之前,必须遍历所有子对象,设置它们的recipe属性为当前Recipe实例:

// 提前声明订阅存储容器
private var cancellables = Set<AnyCancellable>()

func loadData(data: AnyPublisher<[Recipe], any Error>) {
    data
        .sink(
            receiveCompletion: { completion in
                switch completion {
                case .failure(let error):
                    self.logger.error("Error: \(error.localizedDescription)")
                case .finished: break
                }
            },
            receiveValue: { recipes in
                for recipe in recipes {
                    // 先给所有子对象设置反向关联
                    recipe.method?.forEach { $0.recipe = recipe }
                    recipe.ingredients?.forEach { $0.recipe = recipe }
                    
                    // 再插入父对象到上下文
                    self.context.insert(recipe)
                    
                    // 验证关联已建立
                    recipe.method?.forEach { debugPrint($0.step, $0.recipe?.name) }
                }
                // 安全保存上下文
                do {
                    try self.context.save()
                    self.logger.notice("Successfully completed JSON decoding for Recipe:")
                } catch {
                    self.logger.error("Save failed: \(error.localizedDescription)")
                }
            })
        .store(in: &cancellables) // 替换原有的.cancel(),避免订阅提前终止
}

3. 验证关联持久化

修改后重启应用,检查SQL数据库中Method/Ingredient表的外键字段,应该能看到对应Recipe的ID,此时从父视图跳转子视图就能正常显示内容了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 09:42:47