Hello! Welcome back to our series on UI performance.
In our last lesson, we built a complete, professional-grade pipeline for fetching, decoding, and caching images for a UITableView. You learned how to use UITableViewDataSourcePrefetching to start loading image data before a cell scrolls into view, dramatically improving the user experience.
Today, we will solidify that knowledge by applying the exact same pattern to a different, but equally important, UI component. This lesson will show you how to implement UICollectionViewDataSourcePrefetching to proactively load data for upcoming cells.
You'll find that the concepts and even the method names are nearly identical to what you've already learned. This lesson is designed to reinforce the prefetching pattern, ensuring you can apply it confidently to both of UIKit's primary scrolling views.
Why Prefetch? A Quick Refresher
As we've discussed, the key to smooth scrolling is ensuring the main thread is never blocked by expensive work. Prefetching gives us more time to perform tasks like network requests or data processing on a background thread, so the data is ready the moment the UI needs it.

The UICollectionViewDataSourcePrefetching Protocol
Just as UITableView has its own prefetching protocol, so does UICollectionView. The principles are identical. To get a quick, practical overview of how it's implemented, please watch the following video.
Collection View Data Prefetching - raywenderlich.com
This video from Kodeco (formerly raywenderlich.com) provides a concise demonstration of implementing UICollectionViewDataSourcePrefetching from start to finish.
Please watch from 00:15 to 04:16. Pay close attention to: Setting the prefetchDataSource on the collection view. The implementation of the required method, collectionView(_:prefetchItemsAt:). The implementation of the optional cancellation method, collectionView(_:cancelPrefetchingForItemsAt:). The significant visual improvement in scrolling performance after implementation.
As you saw in the video, the protocol is straightforward and consists of two key methods:
-
collectionView(_:prefetchItemsAt indexPaths: [IndexPath])(Required)
This is the core of the prefetching mechanism. The system calls this method when it determines that cells for the given index paths are likely to be needed soon, based on the user's scroll direction and velocity. This is your opportunity to kick off asynchronous data loading operations. TheindexPathsarray is ordered by priority. -
collectionView(_:cancelPrefetchingForItemsAt indexPaths: [IndexPath])(Optional)
If the user changes scroll direction or scrolls very quickly past a set of items, the system will call this method. It's a hint that the data for these index paths is no longer a priority. You should use this to cancel any pending data loading operations to conserve network bandwidth and CPU cycles.
A Complete Implementation Example
Now let's walk through a more detailed, modern code example. The following article provides a complete UIViewController that implements prefetching for a UICollectionView displaying images from a network.
Prefetching Data in UITableView and UICollectionView
This article from Swift Rivals contains an excellent, self-contained example of prefetching in a UICollectionView. We will focus on the code that directly implements the prefetching logic.
Please read the section titled 'Prefetching in UICollectionView'. Study the full code example, from the CustomCollectionViewCell definition down to the fetchImage function. Focus on how the ViewController conforms to UICollectionViewDataSourcePrefetching and how the prefetching and cancellation methods are implemented.
Deconstructing the Pattern
Let's break down the key parts of the code from the Swift Rivals article. This pattern should feel very familiar after our last lesson.
1. Conformance and Setup
First, the view controller must conform to the protocol and assign itself as the prefetchDataSource.
// In your UIViewController subclass
class ViewController: UIViewController, UICollectionViewDataSource, ..., UICollectionViewDataSourcePrefetching {
// In viewDidLoad() or a setup method
private func setupCollectionView() {
// ... other setup ...
collectionView.dataSource = self
collectionView.prefetchDataSource = self // This is the crucial step
// ...
}
}
2. Initiating Fetches in prefetchItemsAt
This method iterates through the index paths provided by the system and starts the download for each one, but only if the data isn't already cached or being downloaded.
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
let item = items[indexPath.item]
// 1. Check if image is already cached or if a download is already active
guard imageCache[item.imageURL] == nil, activeTasks[item.imageURL] == nil else {
continue
}
// 2. If not, start the fetch
fetchImage(for: item.imageURL) { image in
// 3. On completion, update the cache and remove the active task
self.imageCache[item.imageURL] = image
self.activeTasks[item.imageURL] = nil
}
}
}
Note: In the example, fetchImage also stores the URLSessionDataTask in the activeTasks dictionary. This is essential for being able to cancel it later.
3. Handling Cancellation in cancelPrefetchingForItemsAt
When the system tells you an item is no longer needed, you find the corresponding download task and cancel it.
func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths {
let item = items[indexPath.item]
// 1. Find the active task for the item's URL
if let task = activeTasks[item.imageURL] {
// 2. Cancel the task
task.cancel()
// 3. Remove it from the tracking dictionary
activeTasks[item.imageURL] = nil
}
}
}
4. The cellForItemAt Fallback
Remember, prefetching is an optimization, not a guarantee. Your cellForItemAt method must still be able to handle all possible states:
- The image is already in the cache (the ideal pre-fetched case).
- The image is not in the cache (prefetching is still in progress or didn't run).
The example code handles this perfectly:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// ... dequeue cell ...
let item = items[indexPath.item]
// Check the cache
let image = imageCache[item.imageURL]
cell.configure(with: item, image: image)
// If image was not in cache and not being downloaded, start a fetch
if image == nil && activeTasks[item.imageURL] == nil {
fetchImage(for: item.imageURL) { image in
self.imageCache[item.imageURL] = image
// On completion, update the cell ONLY if it's still visible
DispatchQueue.main.async {
if let visibleCell = collectionView.cellForItem(at: indexPath) as? CustomCollectionViewCell {
visibleCell.configure(with: item, image: image)
}
}
}
}
return cell
}
Best Practices
To elevate your implementation from functional to professional, always keep these best practices in mind.
Prefetching Data in UITableView and UICollectionView
The Swift Rivals article concludes with a concise list of best practices. Let's review them to ensure our implementation is robust and efficient.
Read the section 'Best Practices for Prefetching'. Pay particular attention to 'Use Caching', 'Implement Cancellation', and 'Throttle Requests'.
- Caching is Non-Negotiable: Always check a cache before starting a download.
- Always Implement Cancellation: Don't waste resources on data that is no longer needed.
- Throttle Concurrent Requests: Use an
OperationQueuewith a limitedmaxConcurrentOperationCountto avoid overwhelming the network connection, especially on cellular. - Prioritize Visible Cells: The primary fetch in
cellForItemAtshould still work, ensuring your UI is robust even if prefetching doesn't complete in time. - Test with Instruments: Profile your app to confirm that prefetching is improving performance and not introducing new bottlenecks.
Conclusion
Congratulations! You have now mastered the data prefetching pattern for both UITableView and UICollectionView. By understanding the "why" (preventing main thread blockage) and the "how" (the DataSourcePrefetching protocols), you can build highly responsive, professional-grade apps that provide a smooth scrolling experience even with large, complex data sets.
Key Takeaways:
UICollectionViewDataSourcePrefetchingis the direct counterpart to theUITableViewversion, with nearly identical methods (prefetchItemsAt,cancelPrefetchingForItemsAt).- The core implementation pattern is the same:
- Set the
prefetchDataSource. - In
prefetchItemsAt, start asynchronous data loading for upcoming index paths, after checking a cache. - In
cancelPrefetchingForItemsAt, cancel any pending operations for index paths that are no longer needed. - Ensure
cellForItemAtis robust and can handle cases where data has not yet been prefetched.
- Set the
- Adhering to best practices like caching, cancellation, and throttling is essential for a professional implementation.
Preview of the Next Lesson:
We have now thoroughly covered performance optimizations related to data loading for scrolling views. In our next lesson, we will shift our focus back to pure rendering performance by exploring another common bottleneck. We will learn to explain the performance cost of off-screen rendering and custom draw(rect:) implementations, and discover modern, more performant alternatives.