Building reusable list views in Swift UI

Nick Farrant
6 min readDec 31, 2021

--

If you are a mobile developer, it’s an undeniable fact that a big part of the work you do is fetching some data (whether locally or from an API) and displaying it in some fashion. Quite often, this data needs to be represented as a list. Some apps will do this more than others, but it’s pretty much a guarantee that every app will in some way present data in a list.

Lists are pretty basic to build, obviously depending on their functionality. There can also be a lot of boilerplate code, which is why in my most recent efforts to present data in a list, I went out of my way to build reusable and extendable foundations in order to display lists super quickly in the future.

Let’s consider the use case that we will fetch some data from an API and from that data we will display a number of different lists from the objects we get back.

So, taking that use case let’s consider the following JSON responses:

The two response have almost nothing in common, other than they both have aname field. However, we want to be able to display these responses as separate lists with great simplicity. So let’s get started.

The first thing we need to do is map our API responses to Swift objects using codable.

Next, let’s see how we would display these objects in a list at this point. First, we’d need to update both our data models to conform to Identifiable so that we can present them in a list in SwiftUI. This is super simple, we just need to add an id field to the models, and in this example we’ll just set the id to be a new UUID.

We can then build two simple SwiftUI views to display our data.

This is fine and it works, but it’s already becoming clear that if we have lots of types of data that we want to display in a list, we’re going to be writing a lot of duplicated code.

The first step we are going to take in making our lives easier when building lists is a simple protocol to add commonalities to our model objects. We’ll call the protocol ListableResponseModel, and any object that we fetch from the API that we want to display in a list can conform to it.

We need to think about what common traits we want our listable models to have. We’ll definitely want to be able to display a title in the list, and we’ll want all our objects to conform to codable and identifiable. Let’s start with that for now.

Now we have the first iteration of our protocol. We know that every object will have a title at very least, and we can roll other protocol conformances into this one to make things easier. So let’s update our models. Remove any protocol conformance and only conform to ListableResponseModel. You’ll get a compilation error because the models don’t conform to ListableResponseModel, and that’s because we aren’t implementing the titleString property. To fix that, we can extend our models like so:

What we are doing here is returning a field from each particular model as the protocol’s titleString value. We could have returned any string here, but it makes sense for both models that we return the name as the title.

We should now be able to run our project again and see the same results as before. This is good, but we still have a separate view for each list, so let’s fix that next.

You might have thought that we could have a data property on the view that is of type ListableResponseModel, and we can pass in anything that conforms to it, but the above isn’t possible because ListableResponseModelis a protocol, so we need to use generics here. Generics, put simply, is a way of allowing objects of different types that conform to the same protocol to be used in a common way. Let’s see how we’d change the above to use generics.

Now we can initialise our ListView with an array of objects of any type that conforms to ListableResponseModeland display the objects in a list with a title, like so:

Let’s have some fun now and make our list a bit more informative. We’ll extend our ListableResponseModelprotocol to have some extra optional data that it can display.

We’ve added the option to show a subtitle and a detail string in the list if they are set, and we don’t want to have to set these values to nil if our objects don’t support them so instead we can extend the protocol itself to provide default values. Objects that support the fields can override this value.

So let’s now update our ListView to display these fields if they are available.

And with around 20 lines of code, we have a generic view that we can reuse with any type of data we want to without all the boilerplate of multiple views.

Sections

What if we wanted our list to have sections? It’s a very common use case, so let’s look at extending this example to support sections.

We’ll want a new type, which we’ll call ListSection. This object will need to be generic in order to have an array of objects conforming to ListableResponseModel. Our sections will need to be Identifiable so we can display them in a list, and will have an optional header and footer.

Easy! Now we want to be able to use our ListableResponseModelobjects to define what the section’s will be, so let’s extend the protocol remembering that we can provide a default value in the protocol’s extension.

For this example, we just want to give our Prime Ministers view some sections, but leave our iPhone models view as it is. I want the Prime Ministers list to be sectioned by their political party.

I add all the parties that I might anticipate coming back from our API response (we could and should use an enum here in a real-world scenario!).

The next thing to do is to let our response models define what field to use to recognise the section names. So for Prime Ministers, we’ll want the party field. There is a really nice way to do this in Swift using key paths. We are asking our models to provide a field to use as a key path as long as it is a String. Of course, you can change this type if your use case required it. So let’s extend our protocol once again:

And finally, let’s update our PrimeMinister object to define the key path to use for it’s sections:

Great! We just need to update our ListView to support sections now. We still just want to be able to pass our models into the view as they are, mapped from the API, and we can write some logic to build the sections from that data.

Above, we look at the section identifiers that we set for our types, and arrange the objects into sections based on the values that are set in the sectionKey key path.

From here, we can adjust our UI to show data in sections if necessary:

And that’s it! A nice way to make a flexible and reusable list view that can display objects of all types as long as they conform to ListableResponseModel and provide just a titleString.

There’s so much more we can do here:
* Icons
* Actions and navigation
* Search (this is a fun one!)
* Sorting

If you liked this post, let me know and I’ll follow up with the bullet points above to make our list views even more flexible, powerful and above all; reusable, lightweight and simple.

Until next time.

--

--

Nick Farrant

31, Berkshire. Husband to a human, father to a feline. Software Engineer. Will likely write about work, techology and my own life.