I need to edit a persisted SwiftData
item in an isolated context, because if not is isolated with every change triggers expensive computations.
When the edit is done and I save the temporal context. I expect that the temporal context is merged with the main context and all the views that have a dependency with the edited Item.id
be invalidated and updated. But not. When I dismiss the ItemEditView
and temporalContext.save()
is triggered the ItemNormalView
that has a dependency with the Item.id
remains not updated so I have to do a non elegant either reliable workaround.
What is the canonical way of do it?
The most relevant code:
struct ItemEditView: View {
@Bindable var temporalItem: Item
let temporalContext: ModelContext
@Binding var updater: Bool
init(itemID: PersistentIdentifier,
container: ModelContainer,
updater: Binding<Bool>){
temporalContext = ModelContext(container)
temporalContext.autosaveEnabled = false
self.temporalItem = temporalContext.model(for: itemID) as! Item
self._updater = updater
}
var body: some View {
TextField("", text: $temporalItem.name)
.onDisappear{
Task{
try! temporalContext.save()
//Workaround:
try? await Task.sleep(nanoseconds:1_000_000_000)
updater.toggle()
}
}
}
}
struct ItemView: View {
let item: Item
@Environment(\.modelContext) private var modelContext
@State private var itemEditorViewModalViewIsPresented = false
@State private var updater = false
var body: some View {
VStack{
ItemNornalView(item: item, updater: $updater)
Button("Show Edit Modal View"){
itemEditorViewModalViewIsPresented.toggle()
}
}
.sheet(isPresented: $itemEditorViewModalViewIsPresented){
ItemEditView(itemID: item.id, container: modelContext.container, updater: $updater)
}
}
}
struct ItemNornalView: View {
let item: Item
@Binding var updater: Bool
var body: some View {
let _ = Self._printChanges()
Text("\(updater)").scaleEffect(0).hidden()
Text("Item name: \(item.name)")
}
}
The rest of the code:
struct ContentView: View {
var body: some View {
TabView{
MetricsView().tabItem { Text("Metrics") }
ItemsList().tabItem { Text("List") }
}
}
}
struct MetricsView: View {
@Query private var items: [Item]
var body: some View {
Chart{
BarMark(x: .value("x", "Average"), y: .value("y", expensiveComputation(items: items)))
}
}
private func expensiveComputation(items: [Item])->Double{
//...
return 1.0
}
}
struct ItemsList: View {
@Query private var items: [Item]
@Environment(\.modelContext) private var modelContext
@State var path = NavigationPath()
var body: some View {
let _ = Self._printChanges()
NavigationStack(path: $path) {
List {
ForEach(items) { item in
Button{
path.append(item)
}label:{
ItemRowView(item: item)
}
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
.navigationDestination(for: Item.self){ item in
ItemView(item:item)
}
}
}
private func addItem() {
withAnimation {
let newItem = Item()
modelContext.insert(newItem)
try! modelContext.save()
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
}
}