kuuuurt / multiplatform-paging

Kotlin Multiplatform library for Pagination
Apache License 2.0
251 stars 22 forks source link

Jetpack Compose support #4

Closed joreilly closed 3 years ago

joreilly commented 3 years ago

Should it be possible to use this library with Compose Paging library https://developer.android.com/jetpack/androidx/releases/paging#paging_compose_version_100_2 ?

kuuuurt commented 3 years ago

I'm not really sure. I haven't really deep dived into compose and paging support for compose seems fairly new. Maybe I'd need to create another artifact to support compose for this library

joreilly commented 3 years ago

fwiw this is KMP repo I have that uses Compose (and Compose Paging library) right now....https://github.com/joreilly/MortyComposeKMM . I'll try to take look and see if the library can be hooked up with that.

joreilly commented 3 years ago

ok, looks promising so far.....as a test in https://github.com/joreilly/MortyComposeKMM I added following to MortyRepository

    private val characterPager = Pager<Int, CharacterDetail>(
        clientScope = scope,
        config = PagingConfig(
            pageSize = 10,
            enablePlaceholders = false // Ignored on iOS
        ),
        initialKey = 1, // Key to use when initialized
        prevKey = { _, _ -> null }, // Key for previous page, null means don't load previous pages
        nextKey = { items, currentKey -> currentKey + 1 }, // Key for next page. Use `items` or `currentKey` to get it depending on the pagination strategy
        getItems = { startAt, size ->
            val charactersResponse = getCharacters(startAt)

            // TODO how to get this used in nextKey?
            val nextKey = charactersResponse?.info?.next

            charactersResponse?.resultsFilterNotNull()?.map { it.fragments.characterDetail } ?: emptyList()

        }
    )

    val characterPagingData: CommonFlow<PagingData<CharacterDetail>>
        get() = characterPager.pagingData
            .cachedIn(scope) // cachedIn from AndroidX Paging. on iOS, this is a no-op
            .asCommonFlow() // So that iOS can consume the Flow

Replaced characters in CharacterListsViewModel with

val characters = repository.characterPagingData

and finally the following remains as it is right now in Jetpack Compose code in CharactersListView

val lazyCharacterList = characterListsViewModel.characters.collectAsLazyPagingItems()

One thing I haven't figured out yet is how to hook up nextKey as mentioned in comment above

joreilly commented 3 years ago

Pushed changes made so far to https://github.com/joreilly/MortyComposeKMM/tree/multiplatform-paging branch. Looks very promising!

kuuuurt commented 3 years ago

That's great, @joreilly! Thanks for checking this out. I think the paging-compose artifacts are still worth a look and would probably have better integration with compose. Right now, I'm maintaining this library based on my current project's use cases. Compose is pretty much here already so I probably have to take a look sometime soon. If you have contributions on suggestions on it, feel free to make a PR or continue discussion here!

joreilly commented 3 years ago

One thing I still hadn't addressed above was using next value from response and hooking it in to nextKey callback....that callback has access to items but that doesn't actually have that value....maybe I need to change what's returned from getItems

val nextKey = charactersResponse?.info?.next
kuuuurt commented 3 years ago

What's your pagination strategy? In my use case, I usually just have it as a positional or paged strategy that's why I exposed the currentKey and items. currentKey if you want to increment the page. items if maybe you want to check the length of all the items and query from there or even check the latest ID on the list and again, query from there. From what I understand, you're next key is based off of the response from a request. I think getting from the list is kind of better since you're querying from your current snapshot and what's really at the end of the list.

joreilly commented 3 years ago

This is based btw on GraphQL queries (using Apollo GraphQL library and it's associated paging support). I'll take another look over what can be done using that.

kuuuurt commented 3 years ago

I pushed out v0.3.4 and added a PagingResult to make the prevKey and nextKey configurable on the getItems block. I think it may be a common use case to configure it based on the response. Let me know if it works out.

getItems = { currentKey, size ->
    val items = ... // How you will get the items (API Call or Local DB)
    PagingResult(
        items = items,
        currentKey = currentKey,
        prevKey = { _, _ -> null }, // Key for previous page, null means don't load previous pages
        nextKey = { items, currentKey -> currentKey + 1 } // Key for next page. Use `items` or `currentKey` to get it depending on the pagination strategy
    )
}