We don’t want to be loading a loading image!

Our goal is to encapsulate everything required to display an image, and have a single place where we use it to define each of our app-specific icons and images.

If the image is local it can be displayed right away. If it’s a remote image, we’ll want to provide a loading (and potentially a failure) image to show in its place until it’s downloaded. It’s also important that the loading/failure images are locally available. We don’t want to be loading a loading image!

We’re distinguishing between immediately available images and remote images that require downloading, which we can represent as UIImage and URL respectively.

To implement the kind of constraints we’re looking for we can take advantage of generic types. We can build a descriptor type that encapsulates the image view properties we need, and also holds an image reference that can be used by the consumer to get hold of an image before showing it on screen. We’ll call this type an ImageDescriptor .

We’re not constraining the ImageReference placeholder type here, we’ll leave it up to whoever consumes the image descriptor to declare exactly what types of image references it supports. Let’s take a look at how UIImageView can do this.

UIImageView declares the exact types of image descriptors it supports, which allows it to constrain certain capabilities by specific image reference types. This ensures that while the remote image is being fetched, the loading/failure placeholder images are displayable immediately.

Let’s take a look at the implementation of our UIImageView extension that applies image descriptors.

We’re effectively asynchronously mapping an ImageDescriptor<URL> to an ImageDescriptor<UIImage>

We can apply an ImageDescriptor<UIImage> immediately, but when we’re given an ImageDescriptor<URL> we have to download the image. While that’s loading, we apply our loading image descriptor, and if the fetch fails, we apply our failure image.

You’ll see that we download images using UIImageView.fetchImage(...) and cancel any existing downloads with UIImageView.cancelAnyExistingFetches() . The specifics of these implementations are outside the scope of this post, but they could be anything that allows you to request an image from a URL and cancel any existing requests that are tied to this image view.

As soon as the image is downloaded, we construct a new UIImage -based image descriptor and apply it. We’re effectively asynchronously mapping an ImageDescriptor<URL> to an ImageDescriptor<UIImage> .

Now we have all the mechanics in place, it’s time for the fun stuff: defining our own app-specific image descriptors and applying them!

Wherever something expects an ImageDescriptor<UIImage> we can simply provide .logo .loading or .failure and we’ll be reusing all those carefully configured settings to display the icons. If we want to load and display a product given a URL, we can simply use .productImage(with: urlToProduct) and all our products will be displayed the same!

Let’s see this in action.

If we’re always using the same loading/failure placeholder images across the app then it’s trivial to add default values to those parameters to avoid that repetition.