SwiftData实体名称修改与重量级迁移实现方法咨询
问题背景
我正在从CoreData迁移到SwiftData的项目中,已将CoreData模型(Event、EventDate、Lecture)通过@Model宏转换为SwiftData模型(V3Schema),代码如下:
enum V3Schema: VersionedSchema { static var models: [any PersistentModel.Type] = [Event.self, Lecture.self] static var versionIdentifier = Schema.Version(3, 5, 2) @Model public class Event { var date: Date? var memo: String? var status: Bool? = false var title: String? var lecture: Lecture? public init() { } } @Model public class Lecture { var color: String? var room: String? var title: String? @Relationship(deleteRule: .cascade, inverse: \Event.lecture) var events: [Event]? @Relationship(deleteRule: .cascade, inverse: \LectureTime.lecture) var times: [LectureTime]? public init() { } } }
我希望在新版本(V4Schema)中修改实体名称,对应代码如下:
enum V4Schema: VersionedSchema { static var models: [any PersistentModel.Type] = [LectureModel.self, EventModel.self] static var versionIdentifier = Schema.Version(4, 0, 0) @Model class EventModel { var title: String = "" var status: EventStatus = EventStatus.incomplete var memo: String? var deadline: Deadline? var lecture: LectureModel? struct Deadline: Codable, Comparable { static func < (lhs: V4Schema.EventModel.Deadline, rhs: V4Schema.EventModel.Deadline) -> Bool { lhs.startAt < rhs.startAt } var startAt: Date var endAt: Date var isTimeRequired: Bool } enum EventStatus: Int, Codable { case incomplete = 0 case complete = 1 } init(eventID: String, title: String, memo: String? = nil, date: Deadline? = nil, status: EventStatus = .incomplete) { self.eventID = eventID self.title = title self.memo = memo self.deadline = date self.date = deadline?.startAt self.status = status } } }
我编写了迁移计划处理模型变更,但该计划未被触发(日志未输出),代码如下:
enum ModelMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] = [V3Schema.self, V4Schema.self] static var stages: [MigrationStage] = [migrateV3ToV4] static let migrateV3ToV4 = MigrationStage.custom(fromVersion: V3Schema.self, toVersion: V4Schema.self, willMigrate: { _ in Log.debug(message: "Migration Start") // create new EventModel in V4Schema using Event in V3Schema. }, didMigrate: { _ in Log.debug(message: "Migration Complete") }) }
现咨询以下两个问题:
- 如何在SwiftData中修改实体名称?
- 如何在SwiftData中使用重量级迁移?
问题解答
1. SwiftData中修改实体名称的方法
SwiftData默认用类名作为实体名,修改实体名称需要显式指定新旧实体的映射关系,确保迁移时能识别对应关系:
核心操作
在新的@Model类上,通过originalName参数绑定旧实体的名称,同时保持新类名作为新实体名:
enum V4Schema: VersionedSchema { static var models: [any PersistentModel.Type] = [LectureModel.self, EventModel.self] static var versionIdentifier = Schema.Version(4, 0, 0) // 绑定旧实体名"Event",新实体名为"EventModel" @Model(originalName: "Event") class EventModel { var title: String = "" var status: EventStatus = EventStatus.incomplete var memo: String? var deadline: Deadline? var lecture: LectureModel? struct Deadline: Codable, Comparable { static func < (lhs: V4Schema.EventModel.Deadline, rhs: V4Schema.EventModel.Deadline) -> Bool { lhs.startAt < rhs.startAt } var startAt: Date var endAt: Date var isTimeRequired: Bool } enum EventStatus: Int, Codable { case incomplete = 0 case complete = 1 } init(title: String, memo: String? = nil, deadline: Deadline? = nil, status: EventStatus = .incomplete, lecture: LectureModel? = nil) { self.title = title self.memo = memo self.deadline = deadline self.status = status self.lecture = lecture } } // 绑定旧实体名"Lecture",新实体名为"LectureModel" @Model(originalName: "Lecture") class LectureModel { var color: String? var room: String? var title: String? @Relationship(deleteRule: .cascade, inverse: \EventModel.lecture) var events: [EventModel]? init(color: String? = nil, room: String? = nil, title: String? = nil) { self.color = color self.room = room self.title = title } } }
2. SwiftData中使用重量级迁移(自定义迁移)的正确方式
你的迁移计划未触发,通常是因为未关联到ModelContainer、版本号逻辑错误或迁移逻辑缺失,以下是完整实现步骤:
步骤1:将迁移计划关联到ModelContainer
初始化ModelContainer时必须指定migrationPlan,否则迁移逻辑不会被执行:
let container: ModelContainer do { container = try ModelContainer( for: V4Schema.self, migrationPlan: ModelMigrationPlan.self ) } catch { fatalError("Failed to create ModelContainer: \(error)") }
步骤2:实现完整的自定义迁移逻辑
在willMigrate闭包中手动处理旧数据到新实体的转换,包括属性映射、关联关系处理,并保存上下文:
enum ModelMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] = [V3Schema.self, V4Schema.self] static var stages: [MigrationStage] = [migrateV3ToV4] static let migrateV3ToV4 = MigrationStage.custom( fromVersion: V3Schema.self, toVersion: V4Schema.self, willMigrate: { context in Log.debug(message: "Migration Start") // 读取旧版本的Event数据 guard let oldEvents = try? context.fetch(FetchDescriptor<V3Schema.Event>()) else { return } for oldEvent in oldEvents { // 转换状态:旧Bool? 转为 EventStatus枚举 let status: EventStatus = (oldEvent.status ?? false) ? .complete : .incomplete // 转换Deadline:旧Date? 转为自定义Deadline结构(根据业务逻辑补全默认值) var deadline: EventModel.Deadline? if let date = oldEvent.date { deadline = EventModel.Deadline( startAt: date, endAt: Calendar.current.date(byAdding: .hour, value: 1, to: date) ?? date, isTimeRequired: false ) } // 转换关联的Lecture实体 let newLecture = oldEvent.lecture.map { oldLecture in let lecture = LectureModel( color: oldLecture.color, room: oldLecture.room, title: oldLecture.title ) lecture.events = [EventModel]() return lecture } // 创建新的EventModel并添加到上下文 let newEvent = EventModel( title: oldEvent.title ?? "", memo: oldEvent.memo, deadline: deadline, status: status, lecture: newLecture ) context.insert(newEvent) // 关联Lecture和Event的关系 newLecture?.events?.append(newEvent) } // 保存迁移上下文 try? context.save() }, didMigrate: { _ in Log.debug(message: "Migration Complete") } ) }
步骤3:验证迁移触发条件
- 确保
V4Schema的版本号4.0.0高于V3Schema的3.5.2 - 测试时需先安装旧版本App,再升级到新版本(直接安装新版本不会触发迁移)
- 检查日志工具是否正常工作,确保
Log.debug能输出内容
内容的提问来源于stack exchange,提问作者LeeProgrammer
相关产品推荐
相关产品推荐

