RAC
-based lightweight generic ViewModel to handle paginated lists of items (with pages retrieved asynchronously, typically, but not necessarily, from from REST APIs)
Quick example of how one might use PaginatedListViewModel
:
- 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) } }
- Create
ViewModel
instance:let dependency = SamplePaginatedListViewModelDependency() let paginatedList = PaginatedListViewModel(dependency: dependency)
- Observe viewModel's
items
property:paginatedList.items.producer.startWithNext { items in NSLog("Whole list is now \(items)") }
- 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.
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.
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.
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.
PaginatedListViewModel
also available via Cocoapods. Add the following line to your Podfile:
pod 'PaginatedListViewModel', '0.0.3'
Refer to example project in a collection of samples for other ViewModel based µ-frameworks here.
PaginatedListViewModel
created by Sergey Gavrilyuk @octogavrix.
PaginatedListViewModel
is distributed under MIT license. See LICENSE for more info.