I'm trying to use `Room` with `Paging 3`. I'll exp...
# room
m
I'm trying to use
Room
with
Paging 3
. I'll explain my setup first so the problem is a bit clearer •
database (android module)
has
Room
. It has a
Dao
with
fun getItems(): PagingSource<Int, ItemEntity>
ItemEntity
is internal only to
database
module - I don't want to use it in domain. It is converted to
Item
core (kotlin module)
has
Paging 3
. It wants to do paging using
PagingSource<Int, Item>
(not
ItemEntity
as it has no knowledge of it) •
database
module implements interface that gives
PagingSource<Int, Item>
Now my problem is that I cannot convert from
PagingSource<Int, ItemEntity>
to
PagingSource<Int, Item>
. At least I could not find a way to do it. I don't want to expose
ItemEntity
but I want to do paging with converted model
Item
. How can I do that?e
The only way I could make it work now is if
Dao
exposes
DataSource.Factory<Int, ItemEntity>
and then use
.map { it.toItem() }..asPagingSourceFactory().invoke()
So I have to use
DataSource.Factory
from
Paging 2
to map values
j
What I could see so far is that Paging 3 is coupling all your architecture with its own structure. As you are saying, if you need to get a PagingSource from Room, either it provides you with a way to map your entity types to your model class, or you need to define your paging source in a different layer. What I would do, take a flow out of Room, then define an intermediate layer implementing PagingSource and using that flow to emit your model type using the PagingSource. Then use this new layer in your repository. I guess Room returning a PagingSource will work if you don't need mapping entities to model objects... Who would like to have a clean arch with layer isolation nowadays, uh? :D
m
Well it's still in alpha so hopefully mapping is coming. I was hoping to avoid creating my own
PagingSource
but it does seem like this is the best approach now. Although my "hack" of using Paging 2
DataSource.Factory
that supports mapping and conversion to
PagingSource
kind of works as well 🙂
j
Yeah, nice one. Although I would be sceptical if you need to use something from Paging2 that is not in Paging3... Fingers crossed about release version. In my opinion, Paging 3 is bringing back some of the problems that LiveData had before flow, coupling all your layers with its own type and structure.
d
Can you file an FR against our issue tracker? I'm happy to bring this up for 3.1 timeline. We're still working out the lower level APIs for Paging3 and need to make sure it works well for all cases. https://issuetracker.google.com/issues/new?component=413106&amp;template=1096385 Btw, you can wrap a PagingSource to do the mapping like so:
Copy code
class MappedPagingSource<Key : Any, R : Any, T :Any>(
  val originalPagingSource: PagingSource<Key, T>
  val block: suspend (T) -> R
): PagingSource<Key, R> {
  override suspend fun load(params) {
    val page = originalPagingSource.load(params)
    return LoadResult.Page(
      ...
      data = page.data.map(block),
      ...
  }
}
Actually since this should be a relatively straightforward addition, we're also taking contributions to paging via github and I'm happy to help you get setup and point you in the right direction if you're interested in contributing this kind of wrapper / helper
Otherwise I'm happy to bring it up in discussion separately, but most important thing for paging to focus on is to make sure the base apis are super solid and work for all cases before building the higher level stuff - thanks for your patience while we work out the kinks in alpha!
m
Ah nice I'll take a look. Thanks for your response.
d
Sorry it took awhile for me to see this 🙂
m
Should I still file an FR?
d
Yes, in Mantas' case he started with a PagingSource impl from Room so the sample I put just wraps that, but if you're querying for results directly then that works too
I would generally recommend to use the .map operator on PagingData, but I understand they live in different layers so you can make your own cost-benefit decision there
👌 1