Group Core Data items by category in List in SwiftUI

I have a Core Data container with two entities, a Category and an Item. The Item can have one Category assigned and the Category can be assigned to many Items.

What I need to do is group the items by category in a list in SwiftUI.

The code below doesn't group all items by category, it only shows one item by category. How can I group all items that have the same category assigned under the same category group?

Core Data Entities

Category
Attributes
name
Relationship
items (Type: To Many)
Item
Attributes
name
Relationship
category (Type: To One)

Swiftui

struct ItemsView: View {
let selectedList:List
@EnvironmentObject private var itemSM: ItemServiceModel
var body: some View {
List {
ForEach(itemSM.items) { item in
Section(header: Text(item.category?.name ?? "")) {
ForEach(itemSM.items.filter { $0.category == item.category }) { filteredItem in
Text("\(filteredItem.name ?? "")")
}
}
}
}
.onAppear{
itemSM.loadItems(forList: selectedList)
}
}
}

Service Item Service Model

class ItemServiceModel: ObservableObject{
let manager: CoreDataManager
@Published var items: [Item] = []
func loadItems(forList list: List){
let request = NSFetchRequest<Item>(entityName: "Item")
let sort = NSSortDescriptor(keyPath: \Item.name, ascending: true)
request.sortDescriptors = [sort]
let filter = NSPredicate(format: "list == %@", list)
request.predicate = filter
do{
items = try manager.context.fetch(request)
}catch let error{
print("Error fetching items. \(error.localizedDescription)")
}
}
}

This is what I see, as you can see, only one Fruits & Vegetables section should exist.

We don't see any screen capture here.

Not sure I understand what you are doing:

1. List {
2. ForEach(itemSM.items) { item in
3. Section(header: Text(item.category?.name ?? "")) {
4. ForEach(itemSM.items.filter { $0.category == item.category }) { filteredItem in
5. Text("\(filteredItem.name ?? "")")
6. }
7. }
8. }
9. }

You create a new section for each item.

So you will probably create several sections with the same name.

A solution:

  • create a computed var, an array containing all the different categories

private var categories : [String] { // Create all the categories (only once of course) }

  • replace line 2 to 5 by
ForEach(categories) { catagory in
Section(header: Text(catagory)) {
ForEach(itemSM.items.filter { $0.category == category }) { filteredItem in
Text("\(filteredItem.name ?? "")")

Here what worked for me. It basically returns and displays all categories and their respective items from the specified list.

CategoryServiceModel

func loadCategories(for list: List) -> ([Category], [Item]) {
let request = NSFetchRequest<Category>(entityName: "Category")
let predicate = NSPredicate(format: "ANY items IN %@", list.items ?? [])
request.predicate = predicate
let sort = NSSortDescriptor(keyPath: \Category.name, ascending: true)
request.sortDescriptors = [sort]
do {
let categories = try manager.context.fetch(request)
var categories: [Category] = []
var items: [Item] = []
for category in categories {
categories.append(category)
if let items = category.items?.allObjects as? [Item] {
let filteredItems = items.filter { $0.list == list }
items.append(contentsOf: filteredItems)
}
}
return (categories, items)
} catch let error {
print("Error fetching categories. \(error.localizedDescription)")
return ([], [])
}
}

SwiftUI

List {
ForEach(categorySM.categories) { category in
Section(header:Text(category.name ?? "")){
ForEach(category.items?.allObjects as? [Item] ?? []) { item in
Text("\(item.name ?? "")")
}
}
}
}
.onAppear{
categorySM.loadCategories(for: selectedList)
}

Thanks for feedback.

So, it works correctly now ? If so, don't forget to close the thread by marking my answer as correct. Otherwise please explain what's the remaining issue.

Good continuation.

Group Core Data items by category in List in SwiftUI
 
 
Q