Implement pull to refresh and infinite scrolling on Swift
Take the article Meet Swift! , where it’s shown how to make a simple Swift application, and add some famous and useful things like pull to refresh and infinite scrolling using the built-in language features. To make it even more interesting, add a bit of asynchrony, otherwise the application will freeze each time during the update.

As a basis, we take the above example, so we will just supplement it. First, add 2 variables to the controller class, which will be responsible for the number of cells and for the text displayed in the cells
And modify the cell generation code using these variables
Now we will bind the TableView to the controller so that we can make the necessary manipulations on it. In the interface builder, select the TableView, press cmd + alt + enter and right-click on the window that appears.

Drive the name we will use. We

also add the View component to the TableView, where we will place 2 elements to get the following.

This View is needed to display a notification that an update is in progress, and we need it to be visible only when new data is being downloaded (for this we will use the tableFooterView.hidden property), so we need to hide it at the beginning, and only show it later. You will also need to manually start the animation UIActivityIndicatorView, for this, similarly as above, add the binding


For preliminary preparation of these actions will be enough.
Now you can go directly to the implementation of pull to refresh. Add a new variable of the special class UIRefreshControl to the controller class
In viewDidLoad, add code that initializes this variable and binds it to tableView
Now we need to define the refresh function, which will be called every time when the pull to refresh action is performed. In order for the update to occur in asynchronous mode, we use the following scheme (I will not go into the description of the details, it’s not difficult to figure out the code yourself)
As a result, we get

UPD: If you use the UITableViewController (or better to use it in this and similar cases), then the code will be even simpler. The UITableViewController already has the tableView and refreshControl properties, so you do not need to bind the UITableView manually and you do not need to declare refreshControl in the class. It is enough to write the following code in viewDidLoad and everything will work
With infinite scrolling is a little more complicated, but not by much. In the controller class, add a new variable loadMoreStatus, which will be responsible for protection against repeated updates, if it is already running
Add a code to viewDidLoad that will initially hide the View with information about loading new data
Add a definition for the special function scrollViewDidScroll, which is called every time any scrolling occurs. If we dominate to the end of the list, the loadMore function is called, which implements asynchronous loading of new data
As a result, everything works, the application does not freeze, and the data is added successfully. In

such a simple way, you can implement pull to refresh and infinite scrolling, and of course, due to asynchronous updating, you can, for example, make JSON requests to the server in a simple, synchronous way, and this does not interfere with the application.

Training
As a basis, we take the above example, so we will just supplement it. First, add 2 variables to the controller class, which will be responsible for the number of cells and for the text displayed in the cells
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var countRow = 20
var text = "Habrapost"
And modify the cell generation code using these variables
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
{
return countRow
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
{
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "MyTestSwiftCell")
cell.text = "\(text) \(indexPath.row)"
cell.detailTextLabel.text = "Hi, \(indexPath.row)"
cell.detailTextLabel.textColor = UIColor.purpleColor()
return cell
}
Now we will bind the TableView to the controller so that we can make the necessary manipulations on it. In the interface builder, select the TableView, press cmd + alt + enter and right-click on the window that appears.

Drive the name we will use. We

also add the View component to the TableView, where we will place 2 elements to get the following.

This View is needed to display a notification that an update is in progress, and we need it to be visible only when new data is being downloaded (for this we will use the tableFooterView.hidden property), so we need to hide it at the beginning, and only show it later. You will also need to manually start the animation UIActivityIndicatorView, for this, similarly as above, add the binding


For preliminary preparation of these actions will be enough.
Pull to refresh
Now you can go directly to the implementation of pull to refresh. Add a new variable of the special class UIRefreshControl to the controller class
var refreshControl:UIRefreshControl!
In viewDidLoad, add code that initializes this variable and binds it to tableView
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "Идет обновление...")
refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl)
}
Now we need to define the refresh function, which will be called every time when the pull to refresh action is performed. In order for the update to occur in asynchronous mode, we use the following scheme (I will not go into the description of the details, it’s not difficult to figure out the code yourself)
func refresh(sender:AnyObject) {
refreshBegin("Refresh",
refreshEnd: {(x:Int) -> () in
self.tableView.reloadData()
self.refreshControl.endRefreshing()
})
}
func refreshBegin(newtext:String, refreshEnd:(Int) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
println("refreshing")
self.text = newtext
sleep(2)
dispatch_async(dispatch_get_main_queue()) {
refreshEnd(0)
}
}
}
As a result, we get

UPD: If you use the UITableViewController (or better to use it in this and similar cases), then the code will be even simpler. The UITableViewController already has the tableView and refreshControl properties, so you do not need to bind the UITableView manually and you do not need to declare refreshControl in the class. It is enough to write the following code in viewDidLoad and everything will work
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "Идет обновление...")
refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
}
Infinite scrolling
With infinite scrolling is a little more complicated, but not by much. In the controller class, add a new variable loadMoreStatus, which will be responsible for protection against repeated updates, if it is already running
var loadMoreStatus = false
Add a code to viewDidLoad that will initially hide the View with information about loading new data
override func viewDidLoad() {
super.viewDidLoad()
refreshControl = UIRefreshControl()
refreshControl.attributedTitle = NSAttributedString(string: "Идет обновление...")
refreshControl.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refreshControl)
self.tableView.tableFooterView.hidden = true
}
Add a definition for the special function scrollViewDidScroll, which is called every time any scrolling occurs. If we dominate to the end of the list, the loadMore function is called, which implements asynchronous loading of new data
func scrollViewDidScroll(scrollView: UIScrollView!) {
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
let deltaOffset = maximumOffset - currentOffset
if deltaOffset <= 0 {
loadMore()
}
}
func loadMore() {
if ( !loadMoreStatus ) {
self.loadMoreStatus = true
self.activityIndicator.startAnimating()
self.tableView.tableFooterView.hidden = false
loadMoreBegin("Load more",
loadMoreEnd: {(x:Int) -> () in
self.tableView.reloadData()
self.loadMoreStatus = false
self.activityIndicator.stopAnimating()
self.tableView.tableFooterView.hidden = true
})
}
}
func loadMoreBegin(newtext:String, loadMoreEnd:(Int) -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
println("loadmore")
self.text = newtext
self.countRow += 20
sleep(2)
dispatch_async(dispatch_get_main_queue()) {
loadMoreEnd(0)
}
}
}
As a result, everything works, the application does not freeze, and the data is added successfully. In

such a simple way, you can implement pull to refresh and infinite scrolling, and of course, due to asynchronous updating, you can, for example, make JSON requests to the server in a simple, synchronous way, and this does not interfere with the application.