Skip to content

gavrix/PaginatedListViewModel

Repository files navigation

PaginatedListViewModel

Build Status Carthage compatible Cocoapods

RAC-based lightweight generic ViewModel to handle paginated lists of items (with pages retrieved asynchronously, typically, but not necessarily, from from REST APIs)

Usage

Quick example of how one might use PaginatedListViewModel:

  1. Create dependency:
    struct SamplePaginatedListViewModelDependency: PaginatedListViewModelDependency {
        typealias ListItemType = String
        
        func intialPageSignal() -> SignalProducer<RecusrsivePageSignalPayload<ListItemType>, NSError> {
    
            let count = 10
            
            func makeNextPageSignal(skip: Int) -> SignalProducer<RecusrsivePageSignalPayload<ListItemType>, NSError> {
                return SignalProducer() {
                    sink, _ in
                    // make page a list of Ints turned into String
                    let page = (skip...(skip + count)).map{ "\($0)"}
                    
                    let payload = RecusrsivePageSignalPayload(
                        currentPage: page,
                        nextPageSignal: makeNextPageSignal(skip + count)
                    )
                    
                    sink.sendNext(payload)
                    sink.sendCompleted()
                }
            }
            
            return makeNextPageSignal(0)
        }
    }
  2. Create ViewModel instance:
    let dependency = SamplePaginatedListViewModelDependency()
    let paginatedList = PaginatedListViewModel(dependency: dependency)
  3. Observe viewModel's items property:
    paginatedList.items.producer.startWithNext { items in
        NSLog("Whole list is now \(items)")
    }
  4. Start loading by interacting with ViewModel:
    paginatedList.loadNextPage()

Now, every time you call loadNextPage viewModel will ask it's dependency to to request new page through RAC's signals, which will trigger code generating and sending along new page. More on that mechanism below.

Architecture

PaginatedListViewModel is built entirely on RAC4. It relies on the SignalProducer as input and outputs information in several properties represented as RAC's AnyProperty.

PaginatedListViewModel uses dependency injection to abstract away certain specifics of pages are constructed. It's job is to manage list's consistency as well as supporting correct list state (including loading and lastError properties, which are also observable).

In order to paginate we assume signals requesting next page to be connected. Specifically, we chain them in a recursive manner, using RecusrsivePageSignalPayload for indirection. Idea is very simple here: each signal representing page load sends back the payload which is effectively a pair of objects: current page requested plus signalProducer representing following page request. In order to paginate, PaginatedListViewModel catches following page's SignalProducer when receiving current page, and next time loadNextPage() called it starts that SignalProducer thus kicking off next page's request.

Each time PaginatedListViewModel starts next page's SignalProducer property loading is set to true, and upon that SignalProducer termination (regardless of success or failure) it is set back to false.

Whenever current page's request fails, SignalProducer representing that page's request is not abandoned. Property lastError is set to whatever error was returned from that SignalProducer. Next time loadNextPage is called, PaginatedListViewModel will attempt to start the same SignalProducer that failed last time. Upon start, lastError property is reset to nil.

As one can see, dependency is only used to obtain first page's SignalProducer, as subsequent page's can be obtained from previous page's request. When reset method is called in PaginatedListViewModel, however, this process is started over and next time loadNextPage is called, PaginatedListViewModel will request first page's SignalProducer from provided dependency.

Installation

Direct checkout

Checkout this repository, copy PaginatedListViewModel folder into your project's 3rd party dependencies folder. Then drag PaginatedListViewModel.xcodeproj into your master project. Don't forget to add PaginatedListViewModel in in your master project's Target dependencies build phase.

Carthage

In your Cartfile add the following line:

git "https://github.com/gavrix/PaginatedListViewModel.git" "0.0.1"

There are several ways to include carthage-prepared dependencies into your master project depending on whether you want to include frameworks only source code as well. Please refer to carthage documentation.

Cocoapods

PaginatedListViewModel also available via Cocoapods. Add the following line to your Podfile:

pod 'PaginatedListViewModel', '0.0.3'

Example project.

Refer to example project in a collection of samples for other ViewModel based µ-frameworks here.

Credits

PaginatedListViewModel created by Sergey Gavrilyuk @octogavrix.

License

PaginatedListViewModel is distributed under MIT license. See LICENSE for more info.

About

RAC-based lightweight generic ViewModel to handle paginated lists of items (with pages retrieved asynchronously, typically from from REST APIs)

Resources

License

Stars

Watchers

Forks

Packages

No packages published