I am trying to implement hierarchical CoreData in a SwiftUI List
. I created a new project in Xcode using CoreData and just added a parent(to-one)/children(t-many) relationship to the Item
entity:
I use Codegen Manual/None for Item
and created NSManagedObject
subclasses for Item
. I added an extension to return the children as an Array
(so that I can use it in List
for the children:
parameter):
public var childrenArray: [Item]? {
let set = children as? Set<Item> ?? []
print(set)
return set.sorted(by: { $0.id?.uuidString ?? "NO ID?" < $1.id?.uuidString ?? "NO ID?" })
}
for Item
.
In the ContentView.swift
, I changed it to use a List
with the children:
parameter to show my hierarchical data. I also changed the @FetchRequest
so that only Items
without a parent are obtained:
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], predicate: NSPredicate(format: "parent == nil"), animation: .default) private var items: FetchedResults<Item>
var body: some View {
NavigationView {
List(Array(items), children: \.childrenArray) { item in
NavigationLink {
VStack {
Text(item.id!.uuidString)
}
} label: {
Text(item.id!.uuidString)
}
}
.toolbar {
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
newItem.id = UUID()
// Add a new item and make it a children of first new item
let newItem2 = Item(context: viewContext)
newItem2.timestamp = Date()
newItem2.id = UUID()
newItem.addToChildren(newItem2)
// ------------------------------------------------------
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
The result works, but I get a down-chevron for the item that does not have children:
Reading Apples doc about List I understand that the children array should be nil
to tell the List
to not show a chevron. But I can't figure out how to do that in the childrenArray
function. How would I have to adopt the childrenArray
function to get the right array so that the List
will not show a chevron?
CodePudding user response:
Simply make the computed property return nil by using a guard
statement and return nil if there are no children
guard let set = children as? Set<Item>, set.isEmpty == false else { return nil }
Full code
extension Item {
public var childrenArray: [Item]? {
guard let set = children as? Set<Item>, set.isEmpty == false else { return nil }
return set.sorted(by: { $0.id?.uuidString ?? "NO ID?" < $1.id?.uuidString ?? "NO ID?" })
}
}