Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Note
See #630
Context:
launchPagingStoreDoes Not Reflect Latest Writes in Mutable Store #602Paging Technical Design Doc
1. Motivations
Powerful and extensible solution for paging in KMP projects, under the Mobile Native Foundation. Address these Android Paging limitations:
2. Overview
Modular and flexible architecture. Using builder, reducer, middleware, and post-reducer effect patterns. Unidirectional
data flow. The
Pageris the main component. Actions are dispatched through thePager. ThePagerBuildercreatesthe
Pager. It allows configuration of the paging behavior. ThePagingSourcedefines the data loading logic. ThePageFetchingStrategydetermines when to fetch the next page. ThePageAggregatingStrategycombines loaded pages into a single list. ThePagingReducerhandles state changes based on actions. When an action is dispatched, it goes through the middleware pipeline. The middleware can modify the action. The reducer then updates the state based on the action. After the reducer, we invoke post-reducer effects associated with the action and new state. The updated state is sent back thePagerand emitted to the UI.3. The Actual Design
3.1 Key Components
Pager: The main entry point, responsible for coordinating the paging process and providing access to the pagingstate and data.
PagingSource: Defines the data source and loading logic for paged data.PagingState: Represents the current state of the paging data, including loaded pages, errors, and loading status.PagingAction: Defines the actions that can be dispatched to modify the paging state.PagingReducer: Reduces the paging state based on dispatched actions.PagingMiddleware: Intercepts and modifies paging actions before they reach the reducer.PostReducerEffect: Performs side effects after reducing the paging state.PageFetchingStrategy: Determines when to fetch the next page of data.PageAggregatingStrategy: Aggregates loaded pages into a single list.MutablePagingBuffer: Efficiently stores and retrieves paging data.JobCoordinator: Coordinates the execution of paging-related jobs.QueueManager: Manages the queue of pages to be loaded.PagingStreamProvider: Provides a stream of paging data.PagingKeyFactory: Creates keys for paging data.PagingConfig: Configures the paging behavior.ErrorHandlingStrategy: Defines how to handle errors during the paging process.Logger: Logs paging-related events and actions.3.2 Customizations
Providing many extension points and customization options to tailor behavior. Main customization points:
PagingSource: Developers can implement their ownPagingSourceto define how paged data is loaded from the data source. This allows for integration with different data sources and loading mechanisms.PagingMiddleware: CustomPagingMiddlewarecan be implemented to intercept and modify paging actions before they reach the reducer. This enables preprocessing, logging, or any other custom logic.PagingReducer: ThePagingReducercan be customized to define how the paging state is reduced based on dispatched actions. This allows for fine-grained control over the paging state transitions.PostReducerEffect: CustomPostReducerEffectinstances can be configured to perform side effects after reducing the paging state. This is useful for triggering UI updates, analytics events, or any other necessary actions.PageFetchingStrategy: Developers can implement their ownPageFetchingStrategyto determine when to fetch the next page of data based on the current state and configuration. This allows for customizing the prefetching behavior.PageAggregatingStrategy: CustomPageAggregatingStrategyimplementations can be provided to define how loaded pages are aggregated into a single list. This enables different aggregation strategies based on the specific requirements of the application.ErrorHandlingStrategy: Developers can implement their ownErrorHandlingStrategyto define how errors during the paging process are handled. This allows for custom error handling, retry mechanisms, or fallback behaviors.3.3 Data Flow
Unidirectional data flow. Main steps:
Pageris configured usingPagerBuilderand provided an initial key, flow of anchor position, and paging config.Pagersubscribes to thePagingSourceto receive paging data updates.PagingActionis dispatched, it goes through the configuredPagingMiddlewarechain. This enables interception and modification of the action.PagingReducer, which reduces the currentPagingStatebased on the action and returns a newPagingState.PostReducerEffectinstances are executed, enabling side effects to be performed based on the newPagingState.PagerupdatesPagingStateManagerwith the newPagingState.PageFetchingStrategydetermines when to fetch the next page of data based on thePagingConfigand currentPagingState.QueueManagerenqueues the page key, and theJobCoordinatorcoordinates the execution of the paging job.PagingSourceloads the requested page and emits the loaded data through thePagingStreamProvider.MutablePagingBufferfor efficient retrieval and aggregation.PageAggregatingStrategyaggregates the loaded pages into a single list, which is then emitted through thePagerfor consumption by the UI.4. Sample Code
Configuring the Pager using
PagerBuilder:Observing the paging state and dispatching actions:
pager.state.collect { state -> when (state) { is PagingState.Data.Idle -> { // Update UI with loaded data and provide dispatch callback DataView(pagingItems = state.data) { action: PagingAction.User -> pager.dispatch(action) } } is PagingState.LoadingInitial -> { // Show loading indicator InitialLoadingView() } is PagingState.Error -> { // Handle error state and provide dispatch callback InitialErrorViewCoordinator(errorState = state) { action: PagingAction.User -> pager.dispatch(action) } } } }