UICollectionView DataSourcePrefetching Example and Explanation

    Klevis Davidhi

    UICollectionView DataSourcePrefetching

    Recently, Apple announced a new UICollectionViewDataSource protocol extension called UICollectionViewDataSourcePrefetching. This extension makes it possible to implement two new functions that deliver a completely smooth scrolling performance. This tutorial will show you how to use it.

    You can find the source code on GitHub.

    Setting up the Xcode project

    Open Xcode and create a new project based on the Single View Application template. Before you do anything else, in order to improve the appearance of our application ,select the view and choose Editor -> Embed In -> Navigation Controller. Now drag a Collection View from the Object Library onto the view canvas and adjust its size .

    Notice that into the Collection View is one reusable cell , so click under the Attributes Inspector and give this cell an Identifier, let’s call it foodCell. Drag an UIImageView onto it and adjust its size to fit the content.

    Cell Identifier

    Create a new file, of type Cocoa Touch Class , subclass of UICollectionViewCell, name it collectionViewCell and assign it to the cell.

    collectionViewCell Class

    Connect the UIImageView to the collectionViewCell.swift code and name it ‘foodImage‘. Also , connect the collectionView to ViewController.swift.

    ImageView connection

    Conforming to protocols and writing some lines

    Now , it’s time to write some code in viewController.swift.
    The first thing to do is to conform the class to UICollectionViewDelegate,UICollectionViewDataSource, and UICollectionViewDataSourcePrefetching protocols.

    Declaring protocols

    You should probably see some errors showing up in your sidebar, due to required methods not being implemented yet. We’ll cover that soon, but let’s finish a little setup first.

    We’ll need a data source for this UICollectionView, so let’s just create an array called imageArray, which for illustrative purposes will store 30 images. Outside of any method, create the array like so:

    var imageArray = [UIImage?](repeating: nil, count: 30)

    and a variable to store the base url of a picture like so :

    var baseUrl = URL(string: "https://placehold.it")!

    Same as above create another array of type URLSessionDataTask :

    var tasks = [URLSessionDataTask?](repeating: nil, count: 30)

    The next step is to create two separated functions in order to generate images from dynamically generated urls. The first function requires a parameter which in our case will be the index of each cell and will return an url.

     func urlComponents(index: Int) -> URL {
            var baseUrlComponents = URLComponents(url: baseUrl, resolvingAgainstBaseURL: true)
            baseUrlComponents?.path = "/\(screenSize.width)x\(screenSize.height * 0.3)"
            baseUrlComponents?.query = "text=food \(index)"
            return (baseUrlComponents?.url)!

    The second one is where the downloading process will be executed and it requires the indexPath of each cell as a parameter. The return type of this function will be URLSessionDataTask.

      func getTask(forIndex: IndexPath) -> URLSessionDataTask {
            let imgURL = urlComponents(index: forIndex.row)
            return URLSession.shared.dataTask(with: imgURL) { data, response, error in
                guard let data = data, error == nil else { return }
                DispatchQueue.main.async() {
                    let image = UIImage(data: data)!
                    self.imageArray[forIndex.row] = image
                    self.collectionView.reloadItems(at: [forIndex])

    Make sure you have added these lines in viewDidLoad() method:

    collectionView.dataSource = self
    collectionView.delegate = self
    collectionView.prefetchDataSource = self

    Implementing required methods

    Now , we must implement 3 required methods :

    • collectionView(_:numberOfItemsInSection:)
    • collectionView(_:cellForItemAt:)
    • collectionView(_:prefetchItemsAt:)

    UICollectionView must know how many rows are going to be inside the section, so we’ll return the count of the elements in our array here:

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return imageArray.count

    Before coming to the most important methods, there is only one more thing left to do. Let’s create a method where we can supervise the downloading process.
    Once again the parameter required is the indexPath of the cell.

     func requestImage(forIndex: IndexPath) {
            var task: URLSessionDataTask
            if imageArray[forIndex.row] != nil {
                // Image is already loaded
            if tasks[forIndex.row] != nil
                && tasks[forIndex.row]!.state == URLSessionTask.State.running {
                // Wait for task to finish
            task = getTask(forIndex: forIndex)
            tasks[forIndex.row] = task

    Finally, is time to build and return each cell. So in this method :

        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let foodCell = collectionView.dequeueReusableCell(withReuseIdentifier: "foodCell", for: indexPath) as! collectionViewCell
            if let img = imageArray[indexPath.row] {
                foodCell.foodImage.image = img
            else {
                requestImage(forIndex: indexPath)
            return foodCell

    As we see the requestImage(forIndex: IndexPath) method helps us to observe if an image is already downloaded,is in process or is not downloaded at all.


    In order for your app to have “buttery smooth” performance, you must strive for app animation that performs at 60 frames per second. This means that a given frame of the user interface must be displayed in less than 16.67ms in order for the animation to appear “smooth.” Otherwise, when the frame rate drops lower than that, it’s apparent to the user in the form of a choppy animation.

    Prefetching is a mechanism by which you are notified early before the collection view will need the cells to be displayed, thus providing an opportunity to prepare the cell’s data source in advance of actually creating the cell. Prefetching is provided by the UICollectionViewDataSourcePrefetching protocol and contains two instance functions:

    public protocol UICollectionViewDataSourcePrefetching : NSObjectProtocol {
    // indexPaths are ordered ascending by geometric distance from the collection view
    @available(iOS 10.0, *)
    public func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath])
    // indexPaths that previously were considered as candidates for pre-fetching, but were not actually used; may be a subset of the previous call to -collectionView:prefetchItemsAtIndexPaths:
    @available(iOS 10.0, *)
    optional public func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath])

    Implementing DataSource Prefetching

    The first and required method is called when the Collection View is ready to start building out cells even before they are on the screen. An array of NSIndexPath objects is passed to the function and is used to prepare the data source. In our code we would write something like this:

        func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
            for indexPath in indexPaths{
                requestImage(forIndex: indexPath)

    Implementing Prefetch Cancellation

    As mentioned above, the second method, collectionView(_:cancelPrefetchingForItemsAt:), is an optional feature that allows you to revert or clean up your data source when prefetching for an array of cells has been cancelled by the Collection View. This can happen when scrolling changes direction or becomes too rapid for prefetching to be implemented effectively. So let’s implement the last function in our code:

        func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) {
            for indexPath in indexPaths{
                if let task = tasks[indexPath.row] {
                    if task.state != URLSessionTask.State.canceling {

    There may be times when you want to disable collection view prefetching. This can be done by setting the UICollectionView isPrefetchingEnabled property to false.

    These new functions can also be used in UITableView as well just by implementing UITableViewDataSourcePrefetching protocol.


    Using this protocol and its functions is a good choice . There’s no more need to worry about the performance of cells as they are ready to come to the screen before being seen and with a smooth scrolling performance, our application feels better.