iosswiftswiftuiswiftdataswiftui-sheet

SwiftData: sheet(item:) closes and reopens automatically after inserting @Model with @Query


I’m running into a strange behavior when using SwiftData, @Model, and @Query in combination with sheet(item:).

After inserting a new model and presenting a sheet, the sheet stays up for around 13 seconds, then it automatically dismisses itself and then reopens again.

This is my full minimal reproducible example:

import SwiftData
import SwiftUI

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query(sort: \MyModel.name) private var models: [MyModel]
    @State private var selectedModel: MyModel?
    
    var body: some View {
        NavigationStack {
            List(models) { model in
                Button(model.name) {
                    selectedModel = model
                }
            }
            .navigationTitle("Models")
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button("Add") {
                        let newModel = MyModel(name: "Example")
                        modelContext.insert(newModel)
                        selectedModel = newModel
                    }
                }
            }
            .sheet(item: $selectedModel) { model in
                Button("Editing \(model.name)") {
                    selectedModel = nil
                }
            }
        }
    }
}

@Model
class MyModel {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

@main
struct SOPostApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: MyModel.self)
    }
}

This problem happens without any user interaction.

Is this a known issue or limitation of SwiftData + SwiftUI sheets? What workaround can I use instead?

I would prefer a solution that ideally does not involve modifying the MyModel class.


Solution

  • The Identifiable protocol states the id must be stable and unfortunately @Model's default implementation of id is not stable. As you discovered, it changes between insert and save. We can implement the id as a stable one as we would do for any class like this:

    @Model
    final class MyModel {
        var id: ObjectIdentifier {
            ObjectIdentifier(self)
        }
        ...
    

    Besides fixing this sheet problem it will also improve the performance of List and ForEach because rows won't needlessly be replaced during a save. Hopefully Apple change the id to this in the future so the workaround isn't needed. I submitted feedback FB17585928, feel free to do the same and reference mine.