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

SwiftData保存失败:无法打开default.store及ZCALENDARMODEL表不存在求助

问题描述

使用SwiftData保存模型时遭遇报错,提示“无法打开文件‘default.store’”,同时SQLite返回错误码1,显示不存在ZCALENDARMODEL表。我的应用包含RecipeModel和CalendarModel,需求是将日历每日选中的Recipe不引用原模型,而是与Calendar一起通过SwiftData保存,为此创建了RecipeData结构体,但问题仍未解决。

错误日志

CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x303034540> , I/O error for database at /var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store.  SQLite error code:1, '不存在表: ZCALENDARMODEL',用户信息如下:
    NSFilePath = "/var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store";
    NSSQLiteErrorDomain = 1;
}
SwiftData.DefaultStore save failed with error: Error Domain=NSCocoaErrorDomain Code=256 "无法打开文件“default.store”。" UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store, NSSQLiteErrorDomain=1}

相关代码

CalendarModel 代码

import SwiftUI
import SwiftData

enum CalendarSource: String, Codable {
    case created = "手动创建"
    case imported = "导入"
}

@Model
class CalendarModel: Identifiable, Codable {
    var id: UUID = UUID()
    var name: String
    var startDate: Date
    var endDate: Date
    var recipes: [String: RecipeData] = [:]
    var thumbnailData: Data?
    var source: CalendarSource?

    // 计算属性:获取日期区间的天数
    var daysBetween: Int {
        let days = Calendar.current.dateComponents([.day], from: startDate.midnight, to: endDate.midnight).day ?? 0
        return days + 1
    }

    // 计算属性:获取日期区间内的所有日期
    var allDates: [Date] {
        startDate.midnight.allDates(upTo: endDate.midnight)
    }

    // 计算属性:将缩略图数据转为SwiftUI Image
    var thumbnailImage: Image? {
        if let data = thumbnailData, let uiImage = UIImage(data: data) {
            return Image(uiImage: uiImage)
        } else {
            return nil
        }
    }

    // 初始化方法
    init(name: String, startDate: Date, endDate: Date, thumbnailData: Data? = nil, source: CalendarSource? = .created) {
        self.name = name
        self.startDate = startDate
        self.endDate = endDate
        self.thumbnailData = thumbnailData
        self.source = source
    }

    // 便利初始化方法:复制已有日历
    static func copy(from calendar: CalendarModel) -> CalendarModel {
        let copiedCalendar = CalendarModel(
            name: calendar.name,
            startDate: calendar.startDate,
            endDate: calendar.endDate,
            thumbnailData: calendar.thumbnailData,
            source: calendar.source
        )
        // 复制食谱数据
        copiedCalendar.recipes = calendar.recipes.mapValues { $0 }
        return copiedCalendar
    }

    // Codable 协议实现
    private enum CodingKeys: String, CodingKey {
        case id, name, startDate, endDate, recipes, thumbnailData, source
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(UUID.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
        startDate = try container.decode(Date.self, forKey: .startDate)
        endDate = try container.decode(Date.self, forKey: .endDate)
        recipes = try container.decode([String: RecipeData].self, forKey: .recipes)
        thumbnailData = try container.decodeIfPresent(Data.self, forKey: .thumbnailData)
        source = try container.decodeIfPresent(CalendarSource.self, forKey: .source)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
        try container.encode(startDate, forKey: .startDate)
        try container.encode(endDate, forKey: .endDate)
        try container.encode(recipes, forKey: .recipes)
        try container.encode(thumbnailData, forKey: .thumbnailData)
        try container.encode(source, forKey: .source)
    }
}

RecipeData 代码

import SwiftUI

struct RecipeData: Codable, Identifiable {
    var id: UUID = UUID()
    var name: String
    var ingredients: String
    var steps: String
    var thumbnailData: Data?

    // 计算属性:将缩略图数据转为SwiftUI Image
    var thumbnailImage: Image? {
        if let data = thumbnailData, let uiImage = UIImage(data: data) {
            return Image(uiImage: uiImage)
        } else {
            return nil  // 无图片
        }
    }
    
    // 从RecipeModel初始化
    init(recipe: RecipeModel) {
        self.name = recipe.name
        self.ingredients = recipe.ingredients
        self.steps = recipe.steps
        self.thumbnailData = recipe.thumbnailData
    }
}

RecipeModel 代码

import SwiftUI
import SwiftData

@Model
class RecipeModel: Identifiable, Codable {
    var id: UUID = UUID()
    var name: String
    var ingredients: String
    var steps: String
    var thumbnailData: Data? // 存储缩略图的图片数据
    static let fallbackSymbols = ["book.pages.fill", "carrot.fill", "fork.knife", "stove.fill"]

    // 计算属性:将缩略图数据转为SwiftUI Image
    var thumbnailImage: Image? {
        if let data = thumbnailData, let uiImage = UIImage(data: data) {
            return Image(uiImage: uiImage)
        } else {
            return nil  // 无图片
        }
    }

    // 初始化方法
    init(name: String, ingredients: String = "", steps: String = "", thumbnailData: Data? = nil) {
        self.name = name
        self.ingredients = ingredients
        self.steps = steps
        self.thumbnailData = thumbnailData
    }

    // 复制方法
    func copy() -> RecipeModel {
        RecipeModel(
            name: self.name,
            ingredients: self.ingredients,
            steps: self.steps,
            thumbnailData: self.thumbnailData
        )
    }

    // Codable 协议实现
    private enum CodingKeys: String, CodingKey {
        case id, name, ingredients, steps, thumbnailData
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(UUID.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
        ingredients = try container.decode(String.self, forKey: .ingredients)
        steps = try container.decode(String.self, forKey: .steps)
        thumbnailData = try container.decodeIfPresent(Data.self, forKey: .thumbnailData)
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
        try container.encode(ingredients, forKey: .ingredients)
        try container.encode(steps, forKey: .steps)
        try container.encode(thumbnailData, forKey: .thumbnailData)
    }
}

问题原因与解决办法

  1. 核心原因:SwiftData无法自动生成CalendarModel对应的SQLite表,本质是因为模型中使用了SwiftData不支持的复杂嵌套存储——[String: RecipeData]字典嵌套自定义结构体的形式,SwiftData对这类未标记为@Model的嵌套类型持久化支持有限。

  2. 修复方案:

    • 方案一:将RecipeData转为@Model类
      把RecipeData从结构体改为@Model类,然后在CalendarModel中通过@Relationship关联存储,通过创建独立实例实现“不引用原模型”的需求。
    • 方案二:将RecipeData序列化为Data存储
      保留RecipeData为结构体,修改CalendarModel的recipes字段为[String: Data],存储时将RecipeData序列化为Data,读取时反序列化:
      // 修改CalendarModel中的recipes字段
      var recipes: [String: Data] = [:]
      
      // 添加转换方法
      func setRecipe(_ recipeData: RecipeData, forKey key: String) throws {
          let data = try JSONEncoder().encode(recipeData)
          recipes[key] = data
      }
      
      func getRecipe(forKey key: String) throws -> RecipeData? {
          guard let data = recipes[key] else { return nil }
          return try JSONDecoder().decode(RecipeData.self, from: data)
      }
      
    • 方案三:标记复杂字段为外部存储
      在CalendarModel的recipes字段上添加@Attribute(.externalStorage),告知SwiftData将该字段存储为外部数据:
      @Attribute(.externalStorage)
      var recipes: [String: RecipeData] = [:]
      
      注意:该方案对部分嵌套类型有效,需测试验证。
  3. 额外注意:

    • 测试前删除旧的default.store文件(模拟器重置数据或卸载应用,真机直接重装),避免旧数据库结构干扰。
    • 确保SwiftData容器初始化时包含所有模型:
      let container = try ModelContainer(for: [RecipeModel.self, CalendarModel.self])
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 22:17:32