Reputation: 1623
My requirement is to display the notes in pages using clean architecture along with offline suppport. I am using the Paging library for pagination. And below is the clean architectural diagram for getting notes.
Note: Please open the above image in new tab and zoom to view it clear.
I have four layers UI, UseCase, Repository, and Datasource. I am planning to abstract the internal implementation of the data source. For that, I need to map NotesEntities
to another model before crossing the boundary.
class TimelineDao{
@Transaction
@Query("SELECT * FROM NotesEntities ORDER BY timeStamp DESC")
abstract fun getPagingSourceForNotes(): PagingSource<Int, NotesEntities>
}
Current Implementation:
internal class NotesLocalDataSourceImpl @Inject constructor(
private val notesDao: NotesDao
) : NotesLocalDataSource {
override suspend fun insertNotes(notes: NotesEntities) {
notesDao.insert(NotesEntities)
}
override fun getNotesPagingSource(): PagingSource<Int, NotesEntities> {
return notesDao.getPagingSourceForNotes()
}
}
Expected Implementation:
internal class NotesLocalDataSourceImpl @Inject constructor(
private val notesDao: NotesDao
) : NotesLocalDataSource {
override suspend fun insertNotes(notes: NotesRepositoryModel) {
notesDao.insert(NotesRepositoryModel.toEntity())
}
override fun getNotesPagingSource(): PagingSource<Int, NotesRepositoryModel> {
return notesDao.getPagingSourceForNotes().map{ it.toNotesRepositoryModel() }
}
}
I am having an issue mapping the PagingSource<Int, NotesEntities>
to PagingSource<Int, NotesRespositoryModel>
. As for as I have researched, there is no way to map
PagingSource<Int, NotesEntities>
to PagingSource<Int, NotesRespositoryModel>
Kindly let me know if there is a clean way/ workaround way to map the paging source objects. If anyone is sure if there is no way as of now. Please leave a comment as well.
Please Note: I am aware that paging allows transformation for PagingData
. Below is code snippet that gets notes in pages. It maps NotesEntities
to NotesDomainModel
. But then I want to use NotesRespositoryModel
instead of NotesEntities
in the NotesRespositoryImpl
, abstracting the NotesEntities
within NotesLocalDataSourceImpl
layer.
override fun getPaginatedNotes(): Flow<PagingData<NotesDomainModel>> {
return Pager<Int, NotesEntities>(
config = PagingConfig(pageSize = 10),
remoteMediator = NotesRemoteMediator(localDataSource,remoteDataSource),
pagingSourceFactory = localDataSource.getNotesPagingSource()
).flow.map{ it.toDomainModel() }
}
The solution I have thought of:
Instead of using the PagingSource
in Dao directly, I thought of creating a custom PagingSource, that calls the Dao and maps the NoteEntities
to LocalRepositoryModel
.
But then I need to understand that any updates to the DB will not be reflected in the PagingSource
. I need to handle it internally.
Kindly let me know your thoughts on this.
Upvotes: 8
Views: 827
Reputation: 7278
What about creating an implementation of PagingSource
that forwards all of the calls to the original PagingSource
and performs the mapping, something like this:
class MappingPagingSource<Key: Any, Value: Any, MappedValue: Any>(
private val originalSource: PagingSource<Key, Value>,
private val mapper: (Value) -> MappedValue,
) : PagingSource<Key, MappedValue>() {
override fun getRefreshKey(state: PagingState<Key, MappedValue>): Key? {
return originalSource.getRefreshKey(
PagingState(
pages = emptyList(),
leadingPlaceholderCount = 0,
anchorPosition = state.anchorPosition,
config = state.config,
)
)
}
override suspend fun load(params: LoadParams<Key>): LoadResult<Key, MappedValue> {
val originalResult = originalSource.load(params)
return when (originalResult) {
is LoadResult.Error -> LoadResult.Error(originalResult.throwable)
is LoadResult.Invalid -> LoadResult.Invalid()
is LoadResult.Page -> LoadResult.Page(
data = originalResult.data.map(mapper),
prevKey = originalResult.prevKey,
nextKey = originalResult.nextKey,
)
}
}
override val jumpingSupported: Boolean
get() = originalSource.jumpingSupported
}
Usage would be like this then:
override fun getNotesPagingSource(): PagingSource<Int, NotesRepositoryModel> {
return MappingPagingSource(
originalSource = notesDao.getPagingSourceForNotes(),
mapper = { it.toNotesRepositoryModel() },
)
}
Regarding the empty pages
in PagingState
- mapping all loaded pages back to original value would be too expensive and room's paging implementation is only using anchorPosition
and config.initialLoadSize
anyway - see here and here.
Upvotes: 0