How to create subsections in NSFetchedResultsController

I am creating an expense tracer where Expense

can only belong to one Category

, but can have multiple Tag

s. This is my graph object:

enter image description here

In the screen where I list all expenses in a table, I want the expenses to be grouped by date ( sectionDate

) and then Category

(or, using a segmented control, by Tag

). This is the intended user interface:

enter image description here

I can already NSFetchedResultsController

query all expenses, divide them by date, then by category, but I cannot get (1) the total for the category and (2) the list of expenses in it. How can i do this? This is my current code:

let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult> = {
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Expense")
    fetchRequest.resultType = .dictionaryResultType

    fetchRequest.sortDescriptors = [
        NSSortDescriptor(key: #keyPath(Expense.sectionDate), ascending: false)
    ]

    fetchRequest.propertiesToFetch = [
        #keyPath(Expense.sectionDate),
        #keyPath(Expense.category)
    ]
    fetchRequest.propertiesToGroupBy = [
        #keyPath(Expense.sectionDate),
        #keyPath(Expense.category)
    ]

    let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
                                                              managedObjectContext: Global.coreDataStack.viewContext,
                                                              sectionNameKeyPath: #keyPath(Expense.sectionDate),
                                                              cacheName: nil)
    return fetchedResultsController
}()

      

+3


source to share


2 answers


I appreciate @ pbasdf's answer, but I feel like I'm going to have a hard time wrap my head around a solution after a long time without looking at the code.

Instead of fetching objects Expense

, instead of fetching objects Expense

, I have defined a new object for the subsections themselves ( CategoryGroup

and I will also do TagGroup

) and select those entities. These objects have references to the objects Expense

they contain and Category

or Tag

that represent the group. This is my (partially complete) data model:

enter image description here



And mine is NSFetchedResultsController

now much simpler in code:

let fetchedResultsController: NSFetchedResultsController<CategoryGroup> = {
    let fetchRequest = NSFetchRequest<CategoryGroup>(entityName: "CategoryGroup")
    fetchRequest.sortDescriptors = [
        NSSortDescriptor(key: #keyPath(CategoryGroup.sectionDate), ascending: false)
    ]
    return NSFetchedResultsController(fetchRequest: fetchRequest,
                                      managedObjectContext: Global.coreDataStack.viewContext,
                                      sectionNameKeyPath: #keyPath(CategoryGroup.sectionDate),
                                      cacheName: "CacheName")
}()

      

The downside is that I now have to write additional code to absolutely make sure that the relationships between entities are correctly defined whenever created or updated / deleted Expense

or Category

, but an acceptable tradeoff for me, how is it easier to understand in code.

0


source


A must be warned that I have never done this, but personally I would say about it this way:

  • Don't use propertiesToGroupBy

    : It forces you to use .dictionaryResultType

    , which means you can only access the underlying managed objects by doing a separate fetch.
  • Instead, add another computed property to the appropriate NSManagedObject subclass by concatenating sectionDate

    and category.name

    . This property will be used sectionNameKeyPath

    for both FRC, so FRC will set a section in tableView for each unique combination of section name and category.
  • Add category.name

    as another sort descriptor for the selection underpinning FRC. This ensures that the objects Expense

    retrieved by FRC are in the correct order (i.e. All objects Expense

    with the same section name and category name together).
  • Add a section heading title for each section. The property name

    for a section (from FRC) will include both the section name and the category name. In most cases, you can cut and ignore the sectionDate, displaying only the category name and the corresponding total (see below). But for the very first section, and even for the first section, for any given Date section, add an additional view (to the section header view) that shows the sectionDate and the total for that section Date.
  • Determining whether a given section is the first section for a section is a bit tricky. You can get the name for the previous section and compare sectionDates.
  • To collapse / expand sections, maintain an array containing the compressed / expanded state of each section. If the section is collapsed, return 0 in the tableView numberOfRowsInSection

    datasource method ; if extended usage is used by a figure provided by FRC.
  • For category totals, iterate through the array objects

    for the appropriate section (or use a suitable one .reduce

    to achieve the same).
  • For sectionDate totals, filter fetchedObjects

    for FRC to include only those objects Expense

    for the matching sectionDate

    , then iterate or .reduce the filtered array.


I am happy to add or amend if any of them need clarification.

+1


source







All Articles