Smooth Scrolling with iOS 10 Pre-Fetching API

Have you ever seen an application that stutters while scrolling? If yes, then you would imagine how disappointing the experience is. Imagine you are scrolling and the content of application appears to stutter. You might want to know the reason behind this lag. The answer is application is dropping frames while scrolling.

Now you may wonder that what it means to ensure smooth scrolling, the app needs to display in 60 frames second or second words, the app needs to refresh its content and data every second 60 times which means that every frame has approximately 16 ms are rendering. If a frame takes more than allocated time, no data is show and it is said that the frame decrease.

To achieve a good user experience this time is always ~ 16 ms below. With iOS 10, Apple introduced some optimizations to render and show data on the cell. In this article, I’d like to cover how to use prefetching of data source and to achieve smoother scrolling of UITableView.

Prefetch:

As the name suggests, prefetch can be defined as a process to fetch and store data for later use. The data is pre-loaded or preloaded and shows when it is necessary to show it immediately, without having to call it at the right time. In the case of UITableView, prefetching is an ability to preload cells before they appear on a screen.

Prefetching is very helpful where a large number of images are shown for users. Using prefetching, we do not need to get all the data, but it is only those that are going to be displayed. This way not only facilitates but reduces CPU usage and usage.

UITableViewDataSourcePrefetching

It can define as a protocol that provides a pre-warning of data requirements for a table view, which allows triggers of simultaneous data load operations.

Prefetch data source object is also used in conjunction with the table view’s data source to begin loading data for cells before the tableView(_:cellForRowAt:) data source method is called.

The following moves are needed to add a prefetch data source to a table view:

  • Generate the table view and its data source
  • Generate an object that approves the UITableViewDataSourcePrefetching protocol, and allocate it to the prefetchDataSource property on the table view.
  • Initiate asynchronous loading of the data mandatory for the cells at the definite index paths in your execution of tableView(_:prefetchRowsAt:)
  • Make the cell for display using the prefetched data in your tableView(_:cellForRowAt:) data source process.

Apple’s enactment of prefetching API disclosures UITableViewDataSourcePrefetching protocol with two process. These methods are:

public func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) - (required). This method allows us to initiate the asynchronous loading of the data required for the table view’s cells specified by the [IndexPath] parameter.

This method allows us to initiate the asynchronous loading of the data required for the table view’s cells specified by the [IndexPath] parameter.

optional public func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) - (optional) This method allows us to know that the data for the cells specified by the [IndexPath] parameter is not needed anymore.

and a new UITableView’s property:

@available(iOS 10.0, *) weak open var prefetchDataSource: UITableViewDataSourcePrefetching?

Prefetching is only available in iOS 10 and above. Hence @available is used here.

Loading Data Asynchronously

The tableView(_:prefetchRowsAt:) method is not necessarily called for each cell in the table view. Your execution of tableView(_:cellForRowAt:) need therefore be able to cope with the following potential positions:

  • Data has been full via the prefetch appeal, and is ready to be showed.
  • Data is presently being prefetched, but is not yet available.
  • Data has not yet been requested.

Also above mentioned methods are getting called based on the way user interacts with the application.

  • Initially, tableView(_:prefetchRowsAt:) may not be called for initial visible cells
  • As soon as these rows becomes visible, tableView(_:prefetchRowsAt:) get’s called and it’s param [indexPaths] holds index paths for upto next 10 cells.
  • As user scrolls down, tableView(_:prefetchRowsAt:) get’s called for the next set of cells that are about to display.

Notably, the pre-fetching enabled by default for iOS App Development Services. But if you don’t want to use this feature, it can be disabled by setting isPrefetchingEnabled to false. It is also important to note that prefetching works alongside the cell lifecycle. It means that the code, has already been written can implement prefetching without affecting other code.

Working:

class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() // Do any further setup later loading the view, typically from a nib. tableView.delegate = self tableView.dataSource = self tableView.prefetchDataSource = self tableView.tableFooterView = UIView() } //MARK: UITableView Delegate and DataSource extension ListViewController: UITableViewDelegate, UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 1 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier") as! ViewControllerTableViewCell …… return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { ….. } } //MARK: TableView Prefetch Data Source extension ViewController: UITableViewDataSourcePrefetching { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { print(“prefetchRowsAt : \(indexPaths)") } func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) { print("cancelPrefetchingForRowsAt \(indexPaths)") } }

Using print statements, we would know when the data is getting prefetched and when it is getting canceled.

As in the above example, we haven’t touched the UITableViewDelegate protocol nor UITableViewDataSource protocol’s methods. We have just conformed to prefetchDataSource protocol, extended our class and implemented the UITableViewDataSourcePrefetching protocol’s methods.

Conclusion:

Using pre-fetching APIs, the performance of the app can increase because we do not have to worry about the performance of cells, it is ready to appear before rendering on the screen. It possible to take full advantage of the new and latest features through pre-fetching API implementation and it is possible to provide users with the best possible user experience with a smooth scrolling display.