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) } }
问题原因与解决办法
核心原因:SwiftData无法自动生成
CalendarModel对应的SQLite表,本质是因为模型中使用了SwiftData不支持的复杂嵌套存储——[String: RecipeData]字典嵌套自定义结构体的形式,SwiftData对这类未标记为@Model的嵌套类型持久化支持有限。修复方案:
- 方案一:将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] = [:]
- 方案一:将RecipeData转为@Model类
额外注意:
- 测试前删除旧的
default.store文件(模拟器重置数据或卸载应用,真机直接重装),避免旧数据库结构干扰。 - 确保SwiftData容器初始化时包含所有模型:
let container = try ModelContainer(for: [RecipeModel.self, CalendarModel.self])
- 测试前删除旧的
内容的提问来源于stack exchange,提问作者Damast
相关产品推荐
相关产品推荐

