1 min read
Pagination
The Poq Pagination SDK is a wrapper around Android jetpack Paging 3. This SDK simplifies the implementation of the recommended Poq pagination solution which only supports pagination over the network and not over a database. The Poq pagination solution is URL based and your backend needs to return the pagination matching the network model NetworkPagination.
Start using the Pagination SDK
The platform module includes the Pagination SDK module, but if you need to include it, add it to your build.gradle
:
implementation ("com.poqstudio:pagination:$VERSION_NAME")
Koin modules
Poq Pagination SDK dependencies are injected using Koin. The SDK provides the default dependencies definitions via this Koin module:
paginationModule
Overview
PaginationApiService
is the interface that your ApiService needs to implement which has two methods:getFirstPage
andgetPageByUrl
.PoqPagingSource
is the implementation ofBasePagingSource
, it is a generic implementation ofRxPagingSource
.PoqPagingSourceFactory
is the implementation ofBasePagingSourceFactory
, used to createPoqPagingSource
. It has 3 generics parameters:PaginatedData
: the network model of the data that will be paginated.OutPut
: the network model whichPaginationApiService
returns. It needs to implementPaginationResponse
and it contains thePaginatedData
, theNetworkPagination
and any other data you require outside the pagination.Parameters
: the parameters model, such as an id, which will be forwarded to thePaginationApiService
.
BasePagingDataAdapter
is the adapter responsible for displaying the PagingData.BaseLoadStateAdapter
, and its factoryBaseLoadStateAdapterFactory
, is the adapter responsible for displaying the loading and error states when fetching new pages.
How to add a new pagination implementation
To implement a pagination using the Poq Pagination SDK you need to:
- Create a network
PaginatedData
model that represents the items that are going to be paginated, such asNetworkItem
. - Create a network
OutPut
model implementingPaginationResponse<NetworkItem>
, such asNetworkOutput
. - Create your API service implementing
PaginationApiService
. - Inside your Repository implementation, create the Pager object with the
BasePagingSource
created withBasePagingSourceFactory
:class Repository(private val basePagingSourceFactory: BasePagingSourceFactory<NetworkItem, NetworkFeature, Parameters>,private val networkToDomainFeatureItemMapper: Mapper<FeatureItem, NetworkFeatureItem>,private val networkToDomainFeatureDataMapper: Mapper<FeatureData, NetworkFeature>,) {fun getFeatureModel(): FeatureModel {val basePagingSource = basePagingSourceFactory.create(mapHeaders(),parameters)return FeatureModel(itemList = Pager(config = PagingConfig(pageSize = 48), // Recommended page sizepagingSourceFactory = { basePagingSource }).observable.map { pagingData ->pagingData.map(networkToDomainFeatureItemMapper::map)},extraData = basePagingSource.result.map(networkToDomainFeatureDataMapper::map),)}} - Inside your ViewModel implementation, get your
FeatureModel
, subscribe to the itemList observable usingcachedIn(viewModelScope)
and your extraData observable if you have it:getFeatureModel().apply {compositeDisposable.addAll(itemListList.cachedIn(viewModelScope).subscribe{...},extraData.subscribe{...})} - Inside your Activity/Fragment, create a
ConcatAdapter
from your implementation ofBasePagingDataAdapter
andBaseLoadStateAdapter
:val adapter = FeaturePagingDataAdapter(...)val loadStateAdapter = get<BaseLoadStateAdapterFactory>().create { adapter.retry() }recyclerView.adapter = adapter.withLoadStateFooter(loadStateAdapter) - To display the empty, loading and error states of the first page:
- Use the provided methods in
BasePagingDataAdapter
to attach/detach a CombinedLoadStates listener. Forward theCombinedLoadStates
and a snapshot of the current list to your ViewModel:private var loadStateListener: ((CombinedLoadStates) -> Unit)? = nullfun attachListener(){loadStateListener = { loadStates ->viewModel.submitPaginationData(loadStates,adapter.snapshot().items)}loadStateListener?.let { adapter.attachLoadStateListener(it) }}fun detachListener(){loadStateListener?.let { adapter.detachLoadStateListener(it) }} - In your ViewModel, expose LiveDatas for the different states from the forwarded data:
val isEmptyContent = MutableLiveData<Boolean>()val isLoadingFirstPage = MutableLiveData<Boolean>()val isError = MutableLiveData<Boolean>()fun submitPaginationData(loadStates: CombinedLoadStates,datasetInAdapter: List<FeatureItem>,) {isLoadingFirstPage.postValue(loadStates.refresh is LoadState.Loading)isError.postValue(loadStates.refresh is LoadState.Error)isEmptyContent.postValue(datasetInAdapter.isEmpty())}
- Use the provided methods in
If your network model is not accessed directly, like NetworkReviewDetailList, remember to add @Keep to prevent R8 to minify your class.