Mobile paged requests using IAsyncEnumerable

Ahmed Fouad
3 min readAug 9, 2019

--

One of the best new features in c# 8 is the #IAsyncEnumerable as it provide an asynchronous lazy behavior which will solve a lot of problems.

In this article we will explore how we can use the IAsyncEnumerable to handle the paged request with a very clean declarative programming style.

As an example lets make a UWP app that get Sam Smith Albums from Last.fm and show it in a GridView.

I will use the last.fm c# sdk https://github.com/inflatablefriends/lastfm which contains a nice albums search method

_client.Artist.GetTopTracksAsync(string ArtistName, bool AutoCorrection, int page, int pageSize)

Now lets install 2 additional nuget packages AsyncEnumerableExtensions and AsyncEnumerableExtensions.Standard .

Now lets Build Our Main View Model

public class MainPageViewModel{private LastfmClient _client;public MainPageViewModel(){_client = new LastfmClient("b471c30029ba983849723e5120e7504c", "24c4739698eebb92f792b118095dbdff");Artists = GetArtists().ToIncrementalLoadingCollection();}public ISupportIncrementalLoading Artists { get; }private IAsyncEnumerable<AlbumModel> GetArtists(){return AsyncEnumerableExtensions.Standard.AsyncEnumerableBuilder.FromPaged((i, i1) =>_client.Artist.GetTopTracksAsync("Sam Smith", true, i, i1), tracks => tracks.Select(t =>new AlbumModel(){Name = t.Name,Id = t.Id,Image = t.Images.Large.ToString(),Duration = t.Duration}), (i, tracks) => tracks.TotalItems > i, (tracks, i) => tracks.Count() + i,tracks =>{var pagenumber = (tracks?.Page ?? 0) + 1;return pagenumber;},tracks => 20);}}

The magic happens in 2 places

The first one is GetArtists Function

private IAsyncEnumerable<AlbumModel> GetArtists(){return AsyncEnumerableExtensions.Standard.AsyncEnumerableBuilder.FromPaged((i, i1) =>_client.Artist.GetTopTracksAsync("Sam Smith", true, i, i1), tracks => tracks.Select(t =>new AlbumModel(){Name = t.Name,Id = t.Id,Image = t.Images.Large.ToString(),Duration = t.Duration}), (i, tracks) => tracks.TotalItems > i, (tracks, i) => tracks.Count() + i,tracks =>{var pagenumber = (tracks?.Page ?? 0) + 1;return pagenumber;},tracks => 20);}

Lets check AsyncEnumerableBuilder.FromPaged method parameters

1- a delegate that take page number and page size as input and return the actual data.

2- a mapping function that map the paged response returned from the server to a domain model that can be used by the app.

3- a predicate that take the paged response returned from the server and the current number of downloaded items as input and check if there is more pages or not.

4- a function that take the paged response returned from the server and the current number of downloaded items as input and return the new total number of items usually a simple sum.

5- a function that map take the paged response returned from the server and return next page number.

6- a function that map take the paged response returned from the server and return next page size.

The AsyncEnumerableBuilder.FromPaged use this 6 methods to create IAsyncEnumerable.

The Second Part of the magic is in

Artists = GetArtists().ToIncrementalLoadingCollection();

The ToIncrementalLoadingCollection return an ISupportIncrementalLoading which can be used for lazy data binding in the view.

for better understanding lets check this runtime sequence

1- The initial call ToIncrementalLoadingCollection trigger a request for the first page and show it in the listview.

2- When the user scroll to the end of the listview , the list view ask the ISupportIncrementalLoadingCollection for more data.

3- The ISupportIncrementalLoadingCollection ask the IAsyncEnumerable for more data.

4- The IAsyncEnumerable check if any remaining pages , if yes he will load the data of the next page and pass it to the ISupportIncrementalLoadingCollection to show it in the list.

No more magic :D Here is the xaml binding and the page.xaml.cs , it is a normal binding

<GridView ItemsSource="{x:Bind ViewModel.Artists}"><GridView.ItemTemplate><DataTemplate x:DataType="local:AlbumModel"><Grid Background="Black" Height="100" Width="100"><Image Source="{x:Bind Image}" Margin="0,0,0,30" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" /><TextBlock Text="{x:Bind Name}" VerticalAlignment="Bottom" Foreground="White"/></Grid></DataTemplate></GridView.ItemTemplate></GridView>public sealed partial class MainPage : Page{public MainPage(){this.InitializeComponent();this.ViewModel = new MainPageViewModel();}public MainPageViewModel ViewModel { get; set; }}

Screenshot

--

--

Ahmed Fouad

Hello, I’m Ahmed. I’m a software engineer living in Vienna, Austria. I am a fan of technology, web development, and programming. I’m also interested in xamarin