title: How to properly group CoreData records by category in SwiftUI?
body: <p>I am using CoreData. I have:</p>
  
  <ul>
  <li>1 Entity: Todo</li>
  <li>3 attributes: category (String), date (Date), title (String).</li>
  <li>Module: Current Product Module</li>
  <li>Codegen: Class Definition</li>
  </ul>
  
  <p>I would like to build a simple ToDo app that looks like this:</p>
  
  <pre><code>To do items
  
  Category
  1. 3/26/20 To do item 1
  2. 3/26/20 To do item 2
  
  Category
  1. 3/26/20 To do item 3
  2. 3/27/20 To do item 4
  </code></pre>
  
  <p>I know there are similar questions, but I didn't find an answer on how to set everything using CoreData and SwiftUI.</p>
  
  <p>I have most of the code done. Including adding items, saving to CoreData, deleting items.</p>
  
  <p><strong>ContentView.swift</strong>
  Here I display the To do list. I have added the comments to problematic parts.</p>
  
  <pre><code>import SwiftUI
  
  struct ContentView: View {
      @Environment(\.managedObjectContext) var moc
      @State private var date = Date()
      @FetchRequest(
          entity: Todo.entity(),
          sortDescriptors: [
              NSSortDescriptor(keyPath: \Todo.date, ascending: true)
          ]
      ) var todos: FetchedResults&lt;Todo&gt;
  
      @State private var show_modal: Bool = false
  
  //  let dictionary = Dictionary(grouping: Todo) { $0.category }
  
      var dateFormatter: DateFormatter {
          let formatter = DateFormatter()
          formatter.dateStyle = .short
          return formatter
      }
  
      var body: some View {
          NavigationView {
              List {
  
                  // Here I should sort by category
                  ForEach(self.todos, id: \.title) { todo in
  
                      // Here I should write category name instead of static text
                      Section(header: Text("Category")) {
                          ForEach(Array(self.todos.enumerated()), id: \.element) {(i, todo) in
  
                              NavigationLink(destination: TodoDetailsView(todo: todo)) {
                                  HStack {
                                      Text("\(i+1). ")
                                      Text("\(todo.date ?? Date(), formatter: self.dateFormatter)")
                                      Text(todo.title ?? "")
                                  }
                              }
                          }
                      }
                  }
  
              }
              .navigationBarTitle(Text("To do items"))
              .navigationBarItems(
                  trailing:
                  Button(action: {
                      self.show_modal = true
                  }) {
                      Text("Add")
                  }.sheet(isPresented: self.$show_modal) {
                      TodoAddView().environment(\.managedObjectContext, self.moc)
                  }
              )
                  .listStyle(GroupedListStyle())
          }
      }
  }
  
  struct ContentView_Previews: PreviewProvider {
      static var previews: some View {
          let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
          return ContentView().environment(\.managedObjectContext, context)
      }
  }
  </code></pre>
  
  <p><strong>TodoAddView.swift</strong>
  Here I add new items and save them to CoreData. This works OK.</p>
  
  <pre><code>import SwiftUI
  
  struct TodoAddView: View {
  
      @Environment(\.presentationMode) var presentationMode
      @Environment(\.managedObjectContext) var moc
  
      static let dateFormat: DateFormatter = {
          let formatter = DateFormatter()
          formatter.dateStyle = .medium
          return formatter
      }()
  
      @State private var showDatePicker = false
      @State private var title = ""
      @State private var category = ""
      @State private var date : Date = Date()
  
      var body: some View {
          NavigationView {
  
              VStack {
                  HStack {
                      Button(action: {
                          self.showDatePicker.toggle()
                      }) {
                          Text("\(date, formatter: Self.dateFormat)")
                      }
  
                      Spacer()
                  }
  
                  if self.showDatePicker {
                      DatePicker(
                          selection: $date,
                          displayedComponents: .date,
                          label: { Text("Date") }
                      )
                          .labelsHidden()
                  }
  
                  TextField("title", text: $title)
                  TextField("category", text: $category)
  
                  Spacer()
  
              }
              .padding()
              .navigationBarTitle(Text("Add to do item"))
              .navigationBarItems(
                  leading:
                  Button(action: {
                      self.presentationMode.wrappedValue.dismiss()
                  }) {
                      Text("Cancel")
                  },
  
                  trailing:
                  Button(action: {
  
                      let todo = Todo(context: self.moc)
                      todo.date = self.date
                      todo.title = self.title
                      todo.category = self.category
  
                      do {
                          try self.moc.save()
                      }catch{
                          print(error)
                      }
  
                      self.presentationMode.wrappedValue.dismiss()
                  }) {
                      Text("Done")
                  }
              )
          }
      }
  }
  
  struct TodoAddView_Previews: PreviewProvider {
      static var previews: some View {
          TodoAddView()
      }
  }
  </code></pre>
  
  <p><strong>TodoDetailsView.swift</strong>
  Here I display details of each item and can delete an item from there. This works good too.</p>
  
  <pre><code>import SwiftUI
  
  struct TodoDetailsView: View {
  
      @Environment(\.managedObjectContext) var moc
      @Environment(\.presentationMode) var presentationMode: Binding&lt;PresentationMode&gt;
      var todo: Todo
  
      static let dateFormat: DateFormatter = {
          let formatter = DateFormatter()
          formatter.dateStyle = .medium
          return formatter
      }()
  
      @State private var showDatePicker = false
      @State private var newDate : Date = Date()
      @State private var newTitle = ""
      @State private var newCategory = ""
  
      var body: some View {
          ScrollView {
  
              VStack {
                  HStack {
                      Button(action: {
                          self.showDatePicker.toggle()
                      }) {
                          Text("\(newDate, formatter: Self.dateFormat)")
                      }
  
                      Spacer()
                  }
  
                  if self.showDatePicker {
                      DatePicker(
                          selection: $newDate,
                          displayedComponents: .date,
                          label: { Text("Date") }
                      )
                          .labelsHidden()
                  }
  
                  TextField("title", text: $newTitle, onCommit: {
                      self.todo.title = self.newTitle
                      try? self.moc.save()
                  }
                  )
  
                  TextField("category", text: $newCategory, onCommit: {
                      self.todo.category = self.newCategory
                      try? self.moc.save()
                  }
                  )
  
                  Spacer()
  
              }
              .padding()
              .navigationBarTitle(Text("Details"))
              .navigationBarItems(
                  trailing:
                  Button(action: {
                      self.moc.delete(self.todo)
                      do {
                          try self.moc.save()
                          self.presentationMode.wrappedValue.dismiss()
                      }catch{
                          print(error)
                      }
                  }) {
                      Text("Delete")
                          .foregroundColor(.red)
                  }
              )
          }
          .onAppear {
              self.newDate = self.todo.date ?? Date()
              self.newTitle = self.todo.title ?? ""
              self.newCategory = self.todo.category ?? ""
          }
          .onDisappear {
              self.todo.date = self.newDate
              self.todo.title = self.newTitle
              self.todo.category = self.newCategory
              try? self.moc.save()
          }
      }
  }
  
  struct TodoDetailsView_Previews: PreviewProvider {
      static var previews: some View {
          let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
          let todo = Todo.init(context: context)
          todo.date = Date()
          todo.title = "to do item"
          return TodoDetailsView(todo: todo).environment(\.managedObjectContext, context)
      }
  }
  </code></pre>
  
  <p><strong>The thing I don't know is how to properly group the items by, for example, category.</strong>
  How to do it using Core Data? I assume I need to change Codegen of my Entity and add an extension to group the records. For example something like this:</p>
  
  <pre><code>let dictionary = Dictionary(grouping: Todo) { $0.category }
  </code></pre>
  
  <p>But should it be Manual/None or Category/Extension? And where and which code to use? I know how to create a Subclass.</p>
  
  <p>And later I could probably reference to this dictionary in ForEach in my ContentView. But don't know how exactly.</p>
  
  <p>If my assumptions are wrong, please correct me. Thanks in advance.</p>