Chunking a List by DateComponent in SwiftUI with swift-algorithm
I’m currently doing a technical exploration of an idea I have. This idea involves a list view of journal entries a user enters over a long period of time, with new entries being added every week or so.
When thinking about the UX of this list and I need to ensure that the list remains easy to scan after the number of items in it grows. I settled on using an approach similar to how the Apple Journal app works, with the list sectioned by the month of the entry’s date.
Design wise this worked quite well, allowing me to find my place in the list easily as I scrolled down the list of items in my prototypes but when it came to the technical investigation this required a little more effort but luckily there’s an awesome Swift package called swift-algorithms
that makes this a little easier.
The package has a couple of methods for chunking an array; you can chunk it into fixed sized chunks with chunked(ofCount:)
, or you can provide some chunking logic with chunked(by:)
or chunked(on:)
, It’s very flexible.
Chunking an Array of journal entries by month
Let’s start with an example data model and view for showing the list, so that we know what it will look like when we render the un-chunked list.
Our journal entry data model will have a Date
associated with it to represent the date that the entry was posted for and it will have a String
that represents the text added for the entry.
We will then have a view that renders the entries, that view will query all the entries and iterate over them to in a single List
.
This results in a view like this where the entries are laid out in descending order.
When there’s only nine entries this is pretty manageable but when it grows it will be hard to quickly scroll to a specific date when we have 5+ years of entries. Creating sections based on the month so will allow the user to use the sections as a quick indicator of where they are.
Let’s begin by adding the swift-algorithms
package. We can do this by using the “Add Package Dependencies” menu item in Xcode. At the time of writing swift-algorithms
latest version is 1.2.0
, it goes without saying that should you install a different version things may not work the same.
Once the package is installed we can then import Algorithms
to use any of the algorithms that swift-algorithms
offers. The chunked
algorithm extends the Array
class so we can access that as a property of our entries
Array.
As we want to chunk our list on month we’ll want to use chunked(on:)
which allows us to define the value to group the Array on. In this case we’ll extract the year and month DateComponent
s from the Entry
date and use this to group the items.
chunked(on:)
returns a 2-dimensional array, with the first item being the value for the chunk and then second item being the items from the original array that fall under that chunk. This returned value however isn’t going to be usable within our List
and ForEach
in our view so we need to transform the values.
I’ve chosen to wrap this into it’s own function. This function performs the chunking and then map
s through the 2-dimensional array to turn the year-month chunking value into a String
to show as the Section
header and to cast the chunked items to an Array so they can be iterated over.
This can then be used in the EntryListView
by creating a variable to call chunkEntries
and then amending the initial ForEach
to iterate over the chunks and create the Section
with the chunk title and an inner ForEach
for the items.
This then gives us a sectioned list, chunked on month of the year, with the month used as a Section
title and the Entry
s that belong to that month being shown under that Section
.
Handling .onDelete()
One of the harder parts of working with the chunked list is how to handle deleting a list item. Normally when deleting an item in the list you’ll set .onDelete(functionToCall)
on the ForEach
and that would provide you with an offset for the element in the list.
This gets more complicated when the list is chunked because that offset will be based on the chunk’s list not the offset in all items.
To solve this I passed in the key used for the chunk in the chunked list (in my case the title
) to the function that would handle deleting the list item and then I found that chunk and got the item at the offset it in it.
Summary
I found the chunked(on:)
extension of swift-algorithms
to provide a really flexible and powerful tool for splitting an Array based on a property of the underlying data.
Being able to easily add a chunked list into my apps empowers me to explore new ideas to build an amazing UX for my users while abstracting away some of the gory details of how the algorithms are working and I really appreciate the productivity boost.