I am using paging library to retrieve data from an api and show them in a list
for this purpose in my repository I have created the method:
fun getArticleList(query: String): Flow<PagingData<ArticleHeaderData>>
in my viewmodel I have created the search method which goes something like this:
override fun search(query: String) {
val lastResult = articleFlow
if (query == lastQuery && lastResult != null)
return
lastQuery = query
searchJob?.cancel()
searchJob = launch {
val newResult: Flow<PagingData<ArticleList>> = repo.getArticleList(query)
.map {
it.insertSeparators { //code to add separators }.cachedIn(this)
articleFlow = newResult
newResult.collectLatest {
articleList.postValue(it)
}
}
}
in order to test my viewmodel I am using the test method PagingData.from
to create a flow to return from my mocked repository like so:
whenever(repo.getArticleList(query)).thenReturn(flowOf(PagingData.from(articles)))
and then I retrieve the actual paging data from the articleList LiveData like so:
val data = vm.articleList.value!!
this returns a PagingData<ArticleList>
object that I would like to verify it contains the data from the service (i.e. the articles
returned by whenever)
the only way I have found to do this is by creating the following extension function:
private val dcb = object : DifferCallback {
override fun onChanged(position: Int, count: Int) {}
override fun onInserted(position: Int, count: Int) {}
override fun onRemoved(position: Int, count: Int) {}
}
suspend fun <T : Any> PagingData<T>.collectData(): List<T> {
val items = mutableListOf<T>()
val dif = object : PagingDataDiffer<T>(dcb, TestDispatchers.Immediate) {
override suspend fun presentNewList(previousList: NullPaddedList<T>, newList: NullPaddedList<T>, newCombinedLoadStates: CombinedLoadStates, lastAccessedIndex: Int): Int? {
for (idx in 0 until newList.size)
items.add(newList.getFromStorage(idx))
return null
}
}
dif.collectFrom(this)
return items
}
which seems to work, but is based on the PagingDataDiffer
class which is marked as @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
so it may not work in the future
is there a better way to either get the flow
from PagingData (which is marked as internal in the library) or get the actual data from it?
I've had the same problem with the Paging3 library, and there not a lot discussions about this library online yet, but as I digging through some the docs, I may found a solution. The scenario I'm facing is trying to determine whether a data list is empty or not within PagingData
, then I'll manipulate the UI base on that.
Here's what I found in the doc, there are two apis in PagingDataAdapter
that have been added in version 3.0.0-alpha04 which is peek()
, and snapshot()
, peek()
gives us a specific list object based on index, whereas snapshot()
gives us the whole list.
So here's what I've done:
lifecycleScope.launch {
//Your PagingData flow submits the data to your RecyclerView adapter
viewModel.allConversations.collectLatest {
adapter.submitData(it)
}
}
lifecycleScope.launch {
//Your adapter's loadStateFlow here
adapter.loadStateFlow.
distinctUntilChangedBy {
it.refresh
}.collect {
//you get all the data here
val list = adapter.snapshot()
...
}
}
Since I just get my hands on the Paging library and Flow
recently, there might be flaws with this approach, let me know if there are better ways!