{{announcement.body}}
{{announcement.title}}

Dynamic Lists With Asynchronous Data Loading in SwiftUI

DZone 's Guide to

Dynamic Lists With Asynchronous Data Loading in SwiftUI

In this article, we discuss how to create dynamic lists with asynchronous data loading with SwiftUI to help you create more robust layouts.

· Web Dev Zone ·
Free Resource

SwiftUI is an exciting new way to create UI on Apple platforms. But, it is still a young technology, and it can sometimes be hard to find information needed to support the implementation of more advanced use cases.

In porting an app to SwiftUI, I’ve been struggling a bit with the List view. List is a very powerful container view that makes it incredibly easy to create table style components.

But if you want to up your game by adding more features, such as dynamically loading more rows as the the user scrolls, or fetching data asynchronously, it gets a little trickier.  That’s when you would like to know more about how List works its magic behind the scenes — but information is scarce about things like cell reuse, redrawing, handling animations, etc.

After some experimentation (and trial and error), I managed to come up with a list that met some more advanced requirements. I also created a few helper components that I would like to share — in case someone else is looking for similar solutions.

List Requirements

The requirements for my list are as follows:

  1. The list should grow dynamically and batch-wise as the user scrolls.
  2. The data on each row is fetched from a (possibly) slow data source — must be possible to be performed asynchronously in the background.
  3. Some row data should be animated in two different situations: a) when data has been fetched and b) whenever a row becomes visible.
  4. Possibility to reset the list and reload data.
  5. Smooth scrolling throughout.

The Solution

I will now walk you through the essential parts of the solution. The complete code is shown at the end of this article and can also be downloaded here: https://github.com/callistaenterprise/SwiftUIListExample.

We start with some of the plumbing, which consists of a few protocols and generic components that may be used as a foundation for any list of this type.

Data Model

First off is the data model, and we begin with the ListDataItem protocol, which should be adopted by the component representing row data. It must have an Int index that is used to determine when more data needs to be loaded, more on this later.

It also contains a function — fetchData — that retrieves data for the row in a second, possibly slower step.

Swift


Next out is the ListDataProvider, a generic class that maintains the actual list of data items, stored in a published property that is used directly by the List view.

The method fetchMoreItemsIfNeeded is called by the view when a new row is presented. It will check if more items need to be fetched and will also initiate the loading of additional data for each row.

The properties itemBatchCount and prefetchMargin can be used to fine-tune the list behavior.

Swift


Views

Now, we come to the second part of the plumbing, which concerns the views. The structure is similar to that of the data model components. There is one protocol,  DynamicListRow, that the row view should adopt and a generic struct,  DynamicList, which is used as the actual list view.

DynamicList may need some further explanation. The generic parameter Row is a custom view that is used to visualize the row in any fashion you would like. The list gets it data  from  listProvider , a property of the type  ListDataProvider  that we defined above.

The onAppear modifier on the row view is there to ensure that more items are fetched as needed by calling fetchMoreItemsIfNeeded every time a row becomes visible.

There is also an id modifier on the list view itself. What is the purpose of that, you may ask? The listID property provides a unique id that remains the same until the list is reset, in which case a new unique id is generated. This ensures that the list is completely redrawn when it is reset. Leaving it out could sometimes cause the list to not fetch enough data when reset, especially for small batch sizes.

Swift


That’s all for the plumbing. Let’s move on to an example of how to use this to create our list.

List Example

Here is an example of how to implement a list using the components we’ve defined above.

To be able to demonstrate how it works with asynchronous data loading, we start with a very simple datastore, called SlowDataStore. It offers a static function that will give us a random amount between 0 and 1 as a publisher, which takes between 0.5 and 2.0 seconds to deliver the value implemented as a usleep on a background queue.

Swift


Next, we create our custom data item in the class MyDataItem  which conforms to the ListDataItem  protocol. It is a quite straightforward implementation of the protocol requirements. Note that dataPublisher needs to be a stored property in order to keep the publisher from the SlowDataStore alive while waiting for the data to arrive.

Swift


The final part of the list is our custom row view, which conforms to the DynamicListRow protocol. The row contains a horizontal stack with three elements:

  • A text view displaying the line number.
  • A text view displaying "Loading..." if data is not yet available, otherwise the amount is displayed.
  • A custom graph bar view that displays the amount graphically. The implementation of GraphBar  is given with the complete solution at the end.

The animation is triggered by the two modifiers on the horizontal stack. The first one,  onReceive, is used when data first arrives from the SlowDataStore. The second one,  onAppear, is used when the row appears and data is already available.

Note: The animation triggered by onReceive  and  onAppear  needs to be mutually exclusive, otherwise the animation will not work correctly. That’s why we test the property  dataIsFetched.

Swift


We now just need to wrap it all up in a container view, as shown below.

Swift


Conclusion

I hope this article contains some useful information that will help you build your own lists based on similar requirements. Please let me know what you think, and feel free to post any questions, comments, or suggestions.

The complete code is given below. You can also download sample XCode-project here: https://github.com/callistaenterprise/SwiftUIListExample.

Complete Source Code

Swift


Topics:
combine, dynamic list, swift, swiftui, tutorial

Published at DZone with permission of Anders Forssell , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}