Pagination in Kotlin Multiplatform Mobile

Create faster loading pages with these strategies

Annas Surdyanto
Better Programming
Published in
5 min readAug 18, 2022

--

Photo by Francesco on Unsplash

Pagination is always unavoidable in a well-performed app not only to have efficient data loads but also to produce a better user experience. By dividing data into separated pages, the app will load faster, and the user will get the data sooner. It will lead to a better conversion rate than non-paging apps that require all data to be loaded.

When it comes to a question,

What’s one of easy ways to do pagination for declaratively constructed UI of a Kotlin Multiplatform Mobile (KMM) project?

There might be various answers, but we have no exact answers regarding practicality and reliability. Reading this article will let you explore the answer you might expect.

Requirement

It is recommended that you understand the KMM project structure. If you are still unfamiliar with it, please read it first here. It is also expected to be familiar with the declarative UI of Android (Jetpack Compose) and iOS (SwiftUI) platforms. Now let’s start with the shared module.

Shared

Since the KMM project is modular, we will start from the shared module. First, we need a data class consisting of the API response. The paginated API response generally has the current showing page, total pages, and message. Below is an example:

API Response

In the data class above, the pageis the current showing page. Meanwhile, themessage is the server status message that exists only if an error occurs. Besides, we have also totalPages showing the number of pages we should load.

To better work with the API fetching result, it is recommended that we wrap the result in a state. The following is the state example:

List State

The state above wraps the API response and error in a shared module so that we just need to make one function in our shared view model that can return both possible results (API response or error occurrence). Now, let’s look at how the shared view model calls the API. Here’s the code:

Shared View Model Class

In the MovieListSharedViewModelabove, we do not apply a try-catch function since it has been handled in the repository so that the loadMoviefunction above will just call the result. This is applied to let us call the function more efficiently in the iOS workspace. Refer here to explore more.

We have now finished working with our shared module regardless of the use case and repository. We can now turn into our Android module to start implementing pagination for the Android platform.

Android

The declarative UI of the Android platform can be implemented using Jetpack Compose. Establishing pagination in Jetpack Compose is quite simple. We can use its paging library, which is easy to implement. To start, add the following dependency to the Android app Gradle.

implementation(“androidx.paging:paging-compose:1.0.0-alpha10”)

Now let’s make a data paging source to be called in our view model:

Data Paging Source

In the load the function above calls the function we have made in the shared view model before. To collect the return result of the sharedViewModel.loadMovie , we use .last() since the loadMovie function is wrapped by Flow. Finally, to make reusable result conditions, the return logic is handled in PagingHelper class below:

Paging Helper

The PagingHelper class above is, indeed, created to have a reusable return function of the paging source load result so that we do not need to recreate return conditions once we have more than one paging source.

The next step is to create our list view model that will hold the list of our paging data so that it is aware of the app life cycle.

Android List View Model

In the example above, the list is directly initialized. However, if we want to let it empty when the view model is initialized, we can set it to var list: Flow<PagingData<Movie>> = flowOf(PagingData.empty()) . This initialization also enables us to modify the list as necessary.

Finally, in our composable function, we can call the list by collecting the list in our view model by viewModel.list.collectAsLazyPagingItems(). To see how it is implemented, check the code below:

However, the list has CombinedLoadStates to be called to handle the error. Again, to have a reusable paging view that holds its error states, I created a custom paging view holder as follows:

Finally, we can now try our Android app. Below is the screenshot if we run the code:

Android Pagination Preview

iOS

We have made the pagination works in Android, and now it’s time to turn it into Xcode for implementing it in iOS. Let’s start by creating the view model or the observable class to be used as a state in our view configuration. Similar to Android, the view model should at least have a data list, a current showing page, and a throwable object that, in this case, I directly set as an error message. The view model should be as follows:

iOS List View Model

In the example above, we have a loading state namely loadingPage that can be used once we need to use it in our UI configuration. Besides, there is also an error message set once we have either an IOException or status message from the server. As a piece of additional information, the sharedViewModel is initialized in the MovieModule configured in iOS main of the KMM project. Refer here to explore more.

Finally, the most important thing here is that we initialize the list differently from the Android view model since we need to use the append function to add the list. That’s all the view model configuration we need. We can now start creating our UI function.

When showing a list of paging data in SwiftUI, we have to use LazyVStack to have better performance. Besides, we need to show a loading view based on the isLastPage state we have in the view model. This is to be used to indicate that the scroll state has reached the last item.

From the above code, we can take a simple assumption on how the view model calls the loadPage function in the List Loading View when it appears. However, once we have reached the last page, the loading view will not appear, and the view model will not call the loadPage function.

What next? Yup, everything is done, and we can now run the app. We should have our app shows the paging data as follows:

iOS Pagination Preview

Conclusion

When working on a multiplatform project, of course, what we expect is to have a two-in-one code for efficiency. In this project, we have applied a load page function in the shared movie list view model, which we can use in both Android Paging Data Source initialized in Android View Model and iOS View Model. However, we discuss only the core functions needed for pagination implementation.

To have a more complete implementation, please refer to the real project below:

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Annas Surdyanto
Annas Surdyanto

Written by Annas Surdyanto

Android and iOS Engineer in Vivere Group

Responses (1)

Write a response