A Core Data model with entity Node having name, createdAt, to-many relationship children and to-one relationship parent (both optional). Using CodeGen Class Definition.
Using a @FetchRequest with a predicate of parent == nil, it's possible to grab the root nodes and subsequently walk the tree using the relationships.
Root nodes CRUD refreshes the view fine, but any modifications to child nodes don't display until restart although changes are saved in Core Data.
Simplest possible example in the code below illustrates the problem with child node deletion. The deletion works in Core Data but the view does not refresh if the deletion is on a child. The view refresh works fine if on a root node.
I'm new to Swift, so my apologies if this is a rather elementary question, but how can the view be refreshed upon changes to the child nodes?
import SwiftUI
import CoreData
extension Node {
    class func count() -> Int {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        let fetchRequest: NSFetchRequest<Node> = Node.fetchRequest()
        do {
            let count = try context.count(for: fetchRequest)
            print("found nodes: \(count)")
            return count
        } catch let error as NSError {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    }
}
struct ContentView: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(entity: Node.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Node.createdAt, ascending: true)], predicate: NSPredicate(format: "parent == nil"))
    var nodes: FetchedResults<Node>
    var body: some View {
        NavigationView {
            List {
                NodeWalkerView(nodes: Array(nodes.map { $0 as Node })  )
            }
            .navigationBarItems(trailing: EditButton())
        }
        .onAppear(perform: { self.loadData() } )
    }
    func loadData() {
        if Node.count() == 0 {
            for i in 0...3 {
                let node = Node(context: self.managedObjectContext)
                node.name = "Node \(i)"
                for j in 0...2 {
                    let child = Node(context: self.managedObjectContext)
                    child.name = "Child \(i).\(j)"
                    node.addToChildren(child)
                    for k in 0...2 {
                        let subchild = Node(context: self.managedObjectContext)
                        subchild.name = "Subchild \(i).\(j).\(k)"
                        child.addToChildren(subchild)
                    }
                }
            }
            do {
                try self.managedObjectContext.save()
            } catch {
                print(error)
            }
        }
    }
}
struct NodeWalkerView: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    var nodes: [Node]
    var body: some View {
        ForEach( self.nodes, id: \.self ) { node in
            NodeListWalkerCellView(node: node)
        }
        .onDelete { (indexSet) in
            let nodeToDelete = self.nodes[indexSet.first!]
            self.managedObjectContext.delete(nodeToDelete)
            do {
                try self.managedObjectContext.save()
            } catch {
                print(error)
            }
        }
    }
}
struct NodeListWalkerCellView: View {
    @ObservedObject var node: Node
    var body: some View {
        Section {
            Text("\(node.name ?? "")")
            if node.children!.count > 0 {
                NodeWalkerView(nodes: node.children?.allObjects as! [Node] )
                .padding(.leading, 30)
            }
        }
    }
}
EDIT:
A trivial but unsatisfying solution is to make NodeListWakerCellView retrieve the children using another @FetchRequest but this feels wrong since the object is already available. Why run another query? But perhaps this is currently the only way to attach the publishing features?
I am wondering if there's another way to use a Combine publisher directly to the children, perhaps within the .map?
struct NodeListWalkerCellView: View {
    @ObservedObject var node: Node
    @FetchRequest var children: FetchedResults<Node>
    init( node: Node ) {
        self.node = node
        self._children = FetchRequest(
            entity: Node.entity(),
            sortDescriptors: [NSSortDescriptor(keyPath: \Node.createdAt, ascending: false)],
            predicate: NSPredicate(format: "%K == %@", #keyPath(Node.parent), node)
        )
    }
    var body: some View {
        Section {
            Text("\(node.name ?? "")")
            if node.children!.count > 0 {
                NodeWalkerView(nodes: children.map({ $0 as Node }) )
                .padding(.leading, 30)
            }
        }
    }
}
 
    
