SwiftPaginator is a block based Swift class that handles pagination for you.
SwiftPaginator is a block-based Swift class that helps manage paginated resources.
Inspired by NMPaginator, an Obj-C class. SwiftPaginator leverages blocks and generics so that subclassing and delegates arenât needed.
Add this to your Podfile:
pod 'SwiftPaginator', '~> 1.0.0'
and run
$> pod install # (or update)
Add this to your Cartfile
github "apocolipse/SwiftPaginator" ~> 1.0.0
and run
$> carthage update # (bootstrap|build)
You can use The Swift Package Manager to
install SwiftPaginator
by adding the proper description to your
Package.swift
file:
import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
targets: [],
dependencies: [
.Package(url: "https://github.com/apocolipse/SwiftPaginator.git", versions: "1.0.0" ..< Version.max)
]
)
Note that the Swift Package Manager is
still in early design and development, for more infomation checkout its
GitHub Page
Copy SwiftPaginator.swift
to your project
Although based on NMPaginator, SwiftPaginator doesnât require subclassing or delegates. The Paginator
class uses Generics and Blocks to handle everything for you.
import SwiftPaginator
let source = [["one", "two"], ["three", "four"]]
Simple example:
let stringPaginator = Paginator<String>(pageSize: 2, fetchHandler: { (paginator, page, pageSize) in
paginator.receivedResults(source[page], total: 4)
}, resultsHandler: { (_, _) in
self.tableView.reloadData()
})
A more complete example:
let stringPaginator = Paginator<String>(pageSize: 2, fetchHandler: {
(paginator: Paginator, page: Int, pageSize: Int) in
// implement how to fetch results, must call paginator.receivedResults(_:total:) or paginator.failed()
if page < source.count {
paginator.receivedResults(source[page], total: 4)
} else {
paginator.failed()
}
}, resultsHandler: { (paginator, results) in
// Handle results
print(results) // results for the given page
print(paginator.results) // all results
}, resetHandler: { (paginator) in
// callback for a reset, Optional
tableView.reloadData()
}, failureHandler: { (paginator) in
// callback for a failure, Optional
self.presentFailureAlert()
})
Declare the property
class ViewController: UIViewController {
var stringPaginator: Paginator<String>?
...
Be sure to call fetchFirstPage()
in viewDidLoad()
, use fetchNextPage()
elsewhere when you need to load more results (i.e. when scrolling to the bottom of a scroll view or tapping a button)
override func viewDidLoad() {
super.viewDidLoad()
stringPaginator = ...
stringPaginator.fetchFirstPage()
}
func loadMoreResults() {
stringPaginator.fetchNextPage()
}
Use fetchFirstPage()
or fetchNextPage()
to invoke a fetch. fetchFirstPage()
calls reset()
then fetchNextPage()
internally.
stringPaginator.fetchNextPage() // Use this one for most cases
stringPaginator.fetchFirstPage() // will reset paginator
Details on how to define fetch behavior below in fetchHandler
NOTE: Paginator
will not allow simultaneous requests. Requests incoming while paginator.requestStatus
is .InProgress
will be ignored.
To reset the paginator and clear all stored results, simply call:
stringPaginator.reset()
Details on the resetHandler
below show how to react to a reset()
The requestStatus
property stores an enum of type RequestStatus
with 3 cases, .Done
, .InProgress
, .None
. Until the 1st page is fetched, the status is .None
, after which it will be .InProgress
while async requests are processing and .Done
otherwise.
All blocks have a paginator: Paginator<T>
parameter, this is a reference to self
called within the paginator so you may use it within the block without scope issues.
All blocks passed in the init method can be accessed and changed after initialization, i.e.
paginator.fetchHandler = ...
paginator.resultsHandler = ...
paginator.resetHandler = ...
paginator.failureHandler = ...
The fetchHandler
block defines the behavior to fetch new pages. It is called internally from fetchNextPage()
.
NOTE: You Must call either paginator.receivedResults(_:total:)
or paginator.failed()
within the fetchHandler
.
paginator.fetchHandler = {
(paginator, page, pageSize) in
APIClient.getResources() { (response, failure) in
if failure {
paginator.failed()
} else {
paginator.receivedResults(response.results, total: response.total)
}
}
}
The resultsHandler
allows you to handle batches of new results coming in.
Although it is required to be defined, it can be empty, i.e.
...
resultsHandler: { (_, _) in },
...
But usually will be used to notify the View Controller to update the UI
...
resultsHandler: { (paginator, results) in
self.handleNewResults(results)
self.tableView.reloadData()
},
...
NOTE: the results
passed to the resultsHandler
are the results for that specific page, to access all results use paginator.results
The resetHandler
allows you to do things like updating the UI or other activities that must be done after the data source has changed. It is optional.
paginator.resetHandler = {
(paginator) in
self.tableView.reloadData()
}
The failureHandler
allows you to react to failures separately from the fetchHandler
. It isnât required, but is a decent way to split logic of fetching and reacting to failures.
paginator.resetHandler = {
(paginator) in
self.presentFailedAlert()
}