If you're just renaming a property, you shouldn't need an explicit migration plan and could instead rely on automatic migration, using the originalName
parameter of the @Attribute
macro:
@Attribute(originalName: "name") let title: String
In terms of how the willMigrate and didMigrate closures work when you do write a custom migration stage, the SwiftData documentation currently isn't helpful, but the equivalent Core Data documentation offers some insight:
The handlers provide an opportunity to prepare the persistent store’s data for the upcoming changes before the stage runs, and perform any cleanup tasks afterward.
For example, to support a migration that changes an optional attribute to be nonoptional, you might assign a handler to the stage’s willMigrateHandler property that sets any nil instances of that attribute to a default value, thereby ensuring the migration succeeds.
The way I understand it is that at its core, a custom stage actually does the same as a lightweight stage, but the two handlers allow you to prepare the data so that the automatic part of the stage can succeed, and to do some clean-up afterwards if you need to.
I guess if you did want to rename a field using a custom stage, you'd have to:
- Add an optional
title
attribute in the V1 model alongside the original name
attribute, to allow you to copy the value over.* - In the willMigrate closure, get all the V1 objects and copy their
name
value into their title
field. - The automatic part of the migration stage can now run safely, making
title
non-optional and removing the name
field.
* Note that if your original model wasn't part of an explicit VersionedSchema, you can't introduce the title
field in your now-explicit V1 schema because SwiftData won't recognise it as being the same as the implicit 1.0.0 schema it had created. To get around this, I had to create an intermediate schema (1.1.0) with the new field, and added a lightweight migration stage (from 1.0.0 to 1.1.0) to introduce the field before the custom stage (from 1.1.0 to 2.0.0) populated it.
Did you get any further with this?
I have the same problem. I need to have access to the source and the destination models to change attribute types and values as part of the migration. How I understand the current MigrationStage logic is that willMigrate gives you access to the source models and didMigrate gives you access to the destination models, but you can't have access to both at the same time, like with the Core Data mapping model and migration policy approach.
Am I wrong?