Home > Net >  Why does @FetchRequest not update and redraw Views on Deletion?
Why does @FetchRequest not update and redraw Views on Deletion?

Time:11-21

Context

I have a pretty basic SwiftUI setup containing of a ListView presenting all Entities and a DetailView presenting just a single of these Entities. The DetailView also has a Delete option.

The problem I have is, that when deleting the Entity, SwiftUI does not navigate back to the ListView, even though the @FetchRequest should update and redraw the View including ChildViews. Instead, it keeps presenting the DetailView but since it deleted the Entity, it only presents the custom-implemented Default ("N/A") Value for its name.


Code

struct ListView: View {
    @FetchRequest(sortDescriptors: []) var entities: FetchedResults<Entity>

    var body: some View {
        NavigationStack {
            ForEach(entities) { entity in
                RowView(entity: entity)
            }
        }
    }
}

struct RowView: View {
    @ObservedObject var entity: Entity

    var body: some View {
        NavigationLink(destination: DetailView(entity: entity)) {
            Text(entity.name)
        }
    }
}

struct DetailView: View {
    @Environment(\.managedObjectContext) private var context
    @ObservedObject var entity: Entity
    
    var body: some View {
        VStack {
            Text(entity.name)
            
            Button(action: { delete() }) { Text("Delete") }
        }
    }

    private func delete() {
        context.delete(entity)
        
        do {
            try context.save()
        } catch let error as NSError {
            print(error)
        }
    }
}

Question

  • Why does SwiftUI not navigate back to the ListView on Deletion even though the @FetchRequest should update? How can I achieve this goal?

CodePudding user response:

The FetchRequest does update, but the navigation link isn't dependent upon the parent's fetch request - it's not like a sheet or fullScreenCover where the isPresented or item bound attributes determine the new view's visibility.

The easiest thing to do is to handle the dismissal yourself using the dismiss environment function:

struct DetailView: View {
    @Environment(\.dismiss) private var dismiss
    @Environment(\.managedObjectContext) private var context

    // ...

    private func delete() {
        dismiss()
        context.delete(entity)
        // ...
    }
}

While dismiss() is more commonly seen with modals like sheets and full screen covers, it also works in navigation stacks to pop the current view off the stack.

  • Related