Implement a dynamic list with empty, error, and loading state

Christian Elies
Jul 14 · 4 min read
List image (Copyright © 2019 Christian Elies)

Update 2019/08/14: Now available as a Swift package 🚀

Motivation

What do I mean by advanced list?

The app I’m currently working on has many dynamic lists which can represent different states, such as empty, error, items or loading.

I implemented a solution, on top of the data-driven framework IGListKit, to be able to create these type of lists in our UIKit app.

Now, I wanted to implement something similar using SwiftUI. Just a few lines of code, right?


Implementation

In this section, we will take a look at my implementation and the different components I created.

ListState

The AdvancedList should be able to represent different states. We can easily define the states with an Enum.

You will see below why I skipped adding an empty case.

ListService

The AdvancedList needs a component which stores and manages the ListState and the items.

With Combine and the data binding feature of SwiftUI, we connect AdvancedList and ListService.

You can change the list state and modify the items through the ListService and the AdvancedList gets updated automatically.

There are two special items in the implementation of ListService: Item and AnyListItem.

Item

We want to be able to show different items in the same list. Each item should be identifiable and should be representable as a SwiftUI view.

To achieve that, I constrained the Item to the Identifiable and the View protocol.

func appendItems<Item: Identifiable>(_ items: [Item]) where Item: View {}

But there is one problem.

We can’t store objects conforming to the Identifiable and to the View protocol in an array inside of the ListService, because the protocols have associated types and generic constraints. That’s why I have to use ‘type erasure’.

I implemented a box type called AnyListItem which erases the type information of the body property (required by View) using AnyView and of the id property (required by Identifiable) using AnyHashable.

ListService uses AnyListItem internally to erase the type information of each item added to the list.

AdvancedList

Finally, let’s take a look at the implementation of the SwiftUI view AdvancedList.

The view renders itself dependent on the current list state and the current items. To do so, the AdvancedList view needs an instance of ListService which manages the list state and the items.

With the use of the ObjectBinding property wrapper on the listService variable, we tell the AdvancedList view to bind to the changes.

Additionally, a user of the AdvancedList view should be able to specify a view for the empty, loading and error state of the list.

I used the ViewBuilder property wrapper on the parameters in the initializer to achieve that.

As I mentioned above, we don’t need an empty case on the ListState enum because we can use isEmpty on the items instead.

There is one more special thing in my implementation:

The error case of the ListState has an associated value of type Error. That way I can pass the error to the error view which can display it if needed.

That’s it! Let’s see how we can use AdvancedList in the field.


Example Usage

In this section, you will see a simple usage example of the AdvancedList SwiftUI view.

Hint: You will find the complete implementation in the GitHub repository linked below.

First, we have to create the items we want to show on the list.

Example items

I created a ContactListItem representing a contact (name, address, etc.).

To add instances of this item to the list, it has to conform to the protocols Identifiable and View.

I did something special here; I want to be able to render this item differently, dependent on its type property.

Take a look at the usage of viewRepresentationType in the body property.

Animated image showing an AdvancedList with ContactItems on iPhone XR

Additionally, I implemented a second item which represents a simple ad.

I did the same as with ContactListItem. The AdListItem has a type which controls the view definition.

Animated image showing an AdvancedList with AdItems on iPhone XR

Example content view

Finally, we take a look at how to use the AdvancedList inside a simple content view.

You might be wondering what the CustomListStateSegmentedControlView is. It’s a helper view to easily change the list state and add random items to the list.

Animated image showing an AdvancedList with different items on iPhone XR

You’ve reached the end! Thank you very much for reading this article. I hope you liked my AdvancedList view and, of course, SwiftUI, as much as I do.


Better Programming

Advice for programmers.

Christian Elies

Written by

Software Engineer iOS @immowelt

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade