Sample Code

Asynchronously Loading Images into Table and Collection Views

Store and fetch images asynchronously to make your app more responsive.

Download

Overview

Caching images can help you make the table and collection views in your app instantiate fast and respond quickly to scrolling. The app in the sample project demonstrates fetching images with URLs. The images are not part the assets catalog and instead are a part of the app bundle to simulate loading each asynchronously by URL. This ensures the user interface remains responsive. This project also supports Mac Catalyst.

Handle Image Loading and Caching

In the sample, the class ImageCache.swift demonstrates a basic mechanism for image loading from a URL with URLSession and caching the downloaded images using NSCache. Views such as UITableView and UICollectionView are subclasses of UIScrollView.

As the user scrolls in a view, the app requests the same image repeatedly. This sample holds onto the relevant completion blocks until the image loads, then passes the image to all of the requesting blocks so the API only has to make one call to fetch an image for a given URL. The following code shows how the sample project consructs a basic caching and loading method:

// Returns the cached image if available, otherwise asynchronously loads and caches it.
public final func load(url: NSURL, completion: @escaping (UIImage?) -> Swift.Void) {
    // Check for a cached image.
    if let cachedImage = image(url: url) {
        completion(cachedImage)
        return
    }
    // In case there are more than one requestor for the image, we append their completion block.
    if loadingResponses[url] != nil {
        loadingResponses[url]?.append(completion)
        return
    } else {
        loadingResponses[url] = [completion]
    }
    // Go fetch the image.
    ImageURLProtocol.urlSession().dataTask(with: url as URL) { (data, response, error) in
        // Check for the error, then data and try to create the image.
        guard let responseData = data, let image = UIImage(data: responseData),
            let blocks = self.loadingResponses[url], error == nil else {
            DispatchQueue.main.async {
                completion(nil)
            }
            return
        }
        // Cache the image.
        self.cachedImages.setObject(image, forKey: url, cost: responseData.count)
        // Iterate over each requestor for the image and pass it back.
        for block in blocks {
            DispatchQueue.main.async {
                block(image)
            }
        }
    }.resume()
}

Update Your Datasource Responsibly

An app that loads all of its data on launch risks running out of memory or terminating for taking too long to complete. Unless the app requires all data to be loaded before operation, load your images as the UI requests them.

Generally the app should wait until the data source requests a cell to fetch and set an image. The sample project demonstrates one approach to fetching and displaying an image on a reusable view :

cell.imgView.image = item.image
ImageCache.publicCache.load(url: item.url as NSURL) { (image) in
    if let img = image, img != item.image {
        var updatedSnapshot = self.dataSource.snapshot()
        self.opQueue.async {
            let item = self.imageObjects[indexPath.row]
            item.image = img
            updatedSnapshot.reloadItems([item])
            self.dataSource.apply(updatedSnapshot, animatingDifferences: true)
        }
    }
}

See Also

Loading and Caching Images

Providing Images for Different Appearances

Supply image resources that work for light and dark appearances and for high-contrast environments.

Configuring and Displaying Symbol Images in Your UI

Create scalable images that integrate well with your app’s text, and adjust the appearance of those images dynamically.

Creating Custom Symbol Images for Your App

Create symbol images and expand on the icons provided by SF Symbols.

init?(named: String, in: Bundle?, compatibleWith: UITraitCollection?)

Creates an image object using the named image asset that is compatible with the specified trait collection.

init?(named: String, in: Bundle?, with: UIImage.Configuration?)

Creates an image object using the named image asset that is compatible with the specified configuration.

init?(named: String)

Creates an image object from the specified named asset.

init(imageLiteralResourceName: String)

Returns the image object associated with the specified resource.

init?(systemName: String, withConfiguration: UIImage.Configuration?)

Creates an image object containing a system symbol image with the specified configuration.

init?(systemName: String, compatibleWith: UITraitCollection?)

Creates an image object containing a system symbol image appropriate for the specified traits.

init?(systemName: String)

Creates an image object containing a system symbol image.