How to handle many Api requests using Paging 3

Following this codelab I have implemented pagination and caching using RemoteMediator (I am calling a simple API that returns a list of news). I am injecting this RemoteMediator to Repository which has method getResultStream() that returns Flow<PagingData<News>>. In ViewModel is function getNews() : Flow<PagingData<News>> and in Fragment I am calling this function and submitting list to RecyclerAdapter.

Now I want to add a new API call that returns news but with search keyword. What is the proper way to do it? Do I have to write all this code again and create a new RemoteMediator? The logic will be the same but now I have to pass a string argument to the Retrofit get function. The result of this call will replace items in RecyclerView, so I will have two sources of data but one Recycler, do I have to create also a MediatorLiveData? (I don’t add any code but if it help I can do it)


One person asked how exactly I did it (Question posted as answer so it is deleted now but maybe it will help someone in future). So, in ViewModel I have this:

// this flow keeps query. If it is null it means that I want to get all News from API. If it is not null it means that I want to make another API request which takes a parameter query
private val _queryFlow: MutableStateFlow<String?> = MutableStateFlow(null)
val queryFlow: StateFlow<String?>
    get() = _queryFlow

// this function set and validate query
fun submitQuery(query: String?)
{
    Timber.d("Submit new search $query")
    _queryFlow.value = when
    {
        query.isNullOrEmpty() -> null
        query.trim()
            .isNotBlank() -> query.trim()
        else -> null
    }
}

// flow of paging data that I am using in RecyclerView
@ExperimentalCoroutinesApi
val homeNewsData = _queryFlow.flatMapLatest {
    searchNews(it)
}


private fun searchNews(
    query: String?
): Flow<PagingData<NewsRecyclerModel>>
{
    return newsRepository.getSearchResultStream(
        query
    )
        // mapping, filtering, separators etc.
        .cachedIn(viewModelScope)
}

NewsRepository has this function used in VM:

fun getSearchResultStream(searchKey: String?): Flow<PagingData<News>>
{
    val pagingSourceFactory = { database.cacheNewsDao.getNews() }
    return Pager(
        config = PagingConfig(
            pageSize = NETWORK_PAGE_SIZE,
            enablePlaceholders = false,
            initialLoadSize = INITIAL_LOAD_SIZE
        ),
        remoteMediator = newsRemoteMediator.apply { this.searchKey = searchKey },
        pagingSourceFactory = pagingSourceFactory
    ).flow
}

NewsRemoteMedietor:

// it keeps submitted query. based on this I can letter define which API request I want to do
var searchKey: String? = null

override suspend fun load(loadType: LoadType, state: PagingState<Int, News>): MediatorResult
{
    \ all logic the same like in codelabs

    try
    {
        val apiResponse =
            if (searchKey.isNullOrEmpty()) // query is null/empty get Trending news
                newsRetrofit.getTrending(
                    state.config.pageSize,
                    page * state.config.pageSize
                )
            else // query is not blank, make API request with search keyword
                newsRetrofit.getSearched(
                    state.config.pageSize,
                    page * state.config.pageSize,
                    searchKey!!
                )
        \ again the same logic like in codelabs
    }
}
I don’t know if it is the best way to do it (probably not) but in my case, it was working

Answer

Assuming you still have one recyclerview, you can do something like:

val queryFlow = MutableStateFlow(startingQuery)
queryFlow.flatMapLatest { query ->
    Pager(..., MyRemoteMediator(query)) {
        MyPagingSource(...)
    }.flow
}