ReaktivePager is a Kotlin multi-platform library that helps handle paginated results in a reactive way.
It is based on this gist from @mttkay. It uses Reaktive by @badoo as the backing Rx library.
Add Bintray repository into your root build.gradle file:
repositories {
maven {
url "https://dl.bintray.com/doublesymmetry/maven"
}
}
Add publication:
implementation 'com.doublesymmetry:reaktive-pager:<latest-version>'
There's two ways to use this library: You can either create a Pager
instance or use the observablePage
method to create a paginated Observable
.
The Pager
class contains an attribute pager
that you can subscribe to. This will trigger onNext
with the first page, and will continue to giving you the next page whenever you call Pager.next()
. It will trigger onComplete
when there is no more results to load.
The method will return an Observable
can subscribe to. This will trigger onNext
with the first page, and will continue to giving you the next page whenever the given trigger is fired. It will trigger onComplete
when there is no more results to load.
typealias Page = List<Int>
class SomePresenter {
private val fetchNextTrigger = PublishSubject<Unit>()
val pager: Pager<Page> = Pager(
nextPage = {
val last = it?.lastOrNull() ?: 0
observableOf(listOf(last + 1, last + 2, last + 3))
},
hasNext = {
val last = it.lastOrNull() ?: return@Pager true
return@Pager last < 10 // arbitrary condition for the demo
}
)
}
// ...
pager.page.subscribe(onNext = { Log.d(it) })
// print [1, 2 ,3]
pager.next() // print [4, 5, 6]
pager.next() // print [7, 8, 9]
pager.next() // print [10, 11, 12]
typealias Page = List<Int>
val nextPage: (Page?) -> Observable<Page> = nextPage@ {
val last = it?.lastOrNull() ?: 0
return@nextPage observableOf(listOf(last + 1, last + 2, last + 3))
}
val hasNext: (page: Page) -> Boolean = hasNext@ {
val last = it.lastOrNull() ?: return@hasNext true
return@hasNext last < 10 // arbitrary condition for the demo
}
val fetchNextTrigger = PublishSubject<Unit>()
val page = observablePage(nextPage, hasNext, fetchNextTrigger)
// ...
page.subscribe(onNext = { Log.d(it) })
// print [1, 2 ,3]
fetchNextTrigger.onNext() // print [4, 5, 6]
fetchNextTrigger.onNext() // print [7, 8, 9]
fetchNextTrigger.onNext() // print [10, 11, 12]
Reaktive
recommends to avoid using Ktor in Kotlin/Native multithreaded environment until multithreaded coroutines, but if you really need to use it we've provided a helper observableFromCoroutineUnsafe
that you can use in your nextPage
declaration.
typealias Page = ...
class SomePresenter {
private val page: Observable<Page> = {
val nextPage: (Page?) -> Observable<Page> = {
// Dispatcher should be defined per platform
observableFromCoroutineUnsafe(Dispatcher) {
return@observableFromCoroutineUnsafe ...
}
}
val hasNext: (page: Page?) -> Boolean = { it?.hasMore == true }
observablePage(nextPage, hasNext, trigger)
}()
}
// ...
page.subscribe(onNext = { Log.d(it) })