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.
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.