SwiftData Migration: Objects Created in Custom Migration Aren't Persisted or Queryable (Repost)

I'm experiencing a critical issue with SwiftData custom migrations where objects created during migration appear to be inserted successfully but aren't persisted or found by queries after migration completes. The migration logs show objects being created, but subsequent queries return zero results.

I'm migrating from schema version V2 to V2_5, which involves:

Renaming Person class to GroupData

Keeping the same data structure but changing the class name while keeping the old class.

Using a custom migration stage to copy data from old to new schema

Below is an extract of my two schema and migration plan:

Environment:

Xcode 16.0, iOS 18.0, Swift 6.0

SchemaV2

enum LinkMapV2: VersionedSchema {
    static let versionIdentifier: Schema.Version = .init(2, 0, 0)
    static var models: [any PersistentModel.Type] {
        [AnnotationData.self, Person.self, History.self]
    }
    
    @Model
    final class Person {
        @Attribute(.unique) var id: UUID
        var name: String
        var photo: String
        var requirement: String
        var statue: Bool
        var annotationId: UUID?
        var number: Int = 0
        
        init(id: UUID = UUID(), name: String = "", photo: String = "", requirement: String = "", status: Bool = false, annotationId: UUID? = nil, number: Int = 0) {
            self.id = id
            self.name = name
            self.photo = photo
            self.requirement = requirement
            self.statue = status
            self.annotationId = annotationId
            self.number = number
        }
    }
}

Schema V2_5

    static let versionIdentifier: Schema.Version = .init(2, 5, 0)
    static var models: [any PersistentModel.Type] {
        [AnnotationData.self, Person.self, GroupData.self, History.self]
    }
    
    // Keep the old Person model for migration
    @Model
    final class Person {
        @Attribute(.unique) var id: UUID
        var name: String
        var photo: String
        var requirement: String
        var statue: Bool
        var annotationId: UUID?
        var number: Int = 0
        
        init(id: UUID = UUID(), name: String = "", photo: String = "", requirement: String = "", status: Bool = false, annotationId: UUID? = nil, number: Int = 0) {
            self.id = id
            self.name = name
            self.photo = photo
            self.requirement = requirement
            self.statue = status
            self.annotationId = annotationId
            self.number = number
        }
    }
    
    // Add the new GroupData model that mirrors Person
    @Model
    final class GroupData {
        @Attribute(.unique) var id: UUID
        var name: String
        var photo: String
        var requirement: String
        var status: Bool
        var annotationId: UUID?
        var number: Int = 0
        
        init(id: UUID = UUID(), name: String = "", photo: String = "", requirement: String = "", status: Bool = false, annotationId: UUID? = nil, number: Int = 0) {
            self.id = id
            self.name = name
            self.photo = photo
            self.requirement = requirement
            self.status = status
            self.annotationId = annotationId
            self.number = number
        }
    }
}

Migration Plan

    static let migrationV2toV2_5 = MigrationStage.custom(
        fromVersion: LinkMapV2.self,
        toVersion: LinkMapV2_5.self,
        willMigrate: { context in
            do {
                let persons = try context.fetch(FetchDescriptor<LinkMapV2.Person>())
                
                print("=== MIGRATION STARTED ===")
                print("Found \(persons.count) Person objects to migrate")
                
                guard !persons.isEmpty else {
                    print("No Person data requires migration")
                    return
                }
                
                for person in persons {
                    print("Migrating Person: '\(person.name)' with ID: \(person.id)")
                    
                    let newGroup = LinkMapV2_5.GroupData(
                        id: person.id, // Keep the same ID
                        name: person.name,
                        photo: person.photo,
                        requirement: person.requirement,
                        status: person.statue,
                        annotationId: person.annotationId,
                        number: person.number
                    )
                    
                    context.insert(newGroup)
                    print("Inserted new GroupData: '\(newGroup.name)'")
                    
                    // Don't delete the old Person yet to avoid issues
                    // context.delete(person)
                }
                
                try context.save()
                print("=== MIGRATION COMPLETED ===")
                print("Successfully migrated \(persons.count) Person objects to GroupData")
                
            } catch {
                print("=== MIGRATION ERROR ===")
                print("Migration failed with error: \(error)")
            }
        },
        didMigrate: { context in
            do {
                // Verify migration in didMigrate phase
                let groups = try context.fetch(FetchDescriptor<LinkMapV2_5.GroupData>())
                let oldPersons = try context.fetch(FetchDescriptor<LinkMapV2_5.Person>())
                
                print("=== MIGRATION VERIFICATION ===")
                print("New GroupData count: \(groups.count)")
                print("Remaining Person count: \(oldPersons.count)")
                
                // Now delete the old Person objects
                for person in oldPersons {
                    context.delete(person)
                }
                
                if !oldPersons.isEmpty {
                    try context.save()
                    print("Cleaned up \(oldPersons.count) old Person objects")
                }
                
                // Print all migrated groups for debugging
                for group in groups {
                    print("Migrated Group: '\(group.name)', Status: \(group.status), Number: \(group.number)")
                }
                
            } catch {
                print("Migration verification error: \(error)")
            }
        }
    )

And I've attached console output below:

Answered by EndSunset in 864349022

https://developer.apple.com/forums/thread/738812 One of the reply of this thread explained this issue.

Accepted Answer

https://developer.apple.com/forums/thread/738812 One of the reply of this thread explained this issue.

SwiftData Migration: Objects Created in Custom Migration Aren't Persisted or Queryable (Repost)
 
 
Q