Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Downloading and Caching images in SwiftUI

Downloading and caching images is an essential part of building apps in Swift. There are several ways of downloading images with 1st party APIs 3rd party libraries. In my experience, every developer has their way of handling remote images since there’s no go-to standard.

SwiftUI introduced AsyncImage as a 1st party solution to displaying remote images, using URLCache underneath. Several 3rd party libraries are still a popular alternative and often come with a SwiftUI implementation ready to use. Let’s dive into the available options.

Using AsyncImage to download remote images

AsyncImage is the default available SwiftUI view that you can use as follows:

struct ContentView: View {
    var body: some View {
        AsyncImage(url: URL(string: "https://picsum.photos/200/300")!)
    }
}

The above code results in a random fetched image every time the view appears:

Downloading and caching an image in SwiftUI is a common use case.
Downloading and caching an image in SwiftUI is an everyday use case.

Note that I’m using the https://picsum.photos/200/300 URL for testing purposes to return a random image. Don’t use this URL for testing your caching implementation since the caching headers for this URL are set up to prevent caching.

AsyncImage uses URLCache underneath, which you can configure as follows:

URLCache.shared.memoryCapacity = 10_000_000 // ~10 MB memory space
URLCache.shared.diskCapacity = 1_000_000_000 // ~1GB disk cache space

AsyncImage would be enough for downloading and caching remote images in many cases. Yet, you might want to use a 3rd party library if you require more advanced image processing options.

How do you stay current as a Swift developer?

Let me do the hard work and join 19,144 developers that stay up to date using my weekly newsletter:

Adding caching support for remote images

When talking about cache, we could decide on two ways of caching: in-memory or on-disk. In my opinion, it’s always better to go for on-disk caching to prevent yourself from filling memory with large images.

Even better would be to pre-process images before caching and lower their size to what you need in the app. You might not control the size of remote images, so you could be getting images with large dimensions. These images are taking more space on disk, which means we would benefit from resizing before caching.

Processing images can be complicated, and it’s not always easy to write performant code. Since downloading the image can already be time-consuming, I want the best performing code for processing tasks. It’s tempting to write the code yourself, but in this case, I would prefer to benefit from the community and pick a 3rd party library with lots of contributions.

Picking a 3rd party library for image processing

Over the years, I’ve been using several 3rd party image libraries. Kingfisher and Nuke are well-maintained popular examples, and both provide performant APIs for downloading, caching, and processing images.

I’m a fan of the Nuke library, so I’m going to demonstrate to you what our code would look like after migrating away from AsyncImage:

struct ContentView: View {
    var body: some View {
        // Before:
        AsyncImage(url: URL(string: "https://picsum.photos/200/300")!)

        // After:
        LazyImage(source: "https://picsum.photos/200/300")
    }
}

Migrating is painless, which has been a focus of many of these 3rd party libraries. Yet, we still need to configure the image cache, which works differently for each library. I recommend reading through the documentation for the library you decide to use.

Shouldn’t I stay away from 3rd party libraries?

I’ve had several discussions throughout my career around whether or not you should reach out for 3rd party dependencies. You’re becoming dependent on externally maintained code with the potential risk of a library becoming outdated.

I prefer to write solutions myself, but that’s not always a possible option. You might be short on time, or you don’t have the experience to write a solution yourself. Furthermore, you can minimize the risks of using 3rd party libraries by carefully deciding which library to use.

When picking a 3rd party library, I try to look at the following points:

  • Maintenance
    Ensure it’s recently updated and does not have many open pull requests that could indicate low maintenance.
  • Popularity
    I prefer a repository with many stars since it indicates a popular and likely often-used dependency.
  • Code quality
    The last thing you want is to add new patterns with learnings curves to your project. I always verify whether the package supports the latest Swift features and whether code patterns align with what we’re used to with the Swift standard library.
  • Dependencies
    When adding a package, don’t be surprised to see many extra fetched dependencies. Someone must maintain each dependency, so think twice when a package depends on other libraries.

If all of the above points look good, I’m convinced it’s an excellent choice to reach out for a 3rd party dependency and benefit from the open-source community. It can save you a lot of time and allows you to focus on building app-specific features instead.

Nuke is an excellent example of such 3rd party dependency for downloading and caching images. It’s well maintained, uses the latest Swift features, does not have many open pull requests, and has over 6K stars. I’ve been using it for several years now and haven’t been disappointed.

Conclusion

Downloading and caching images is a common task when building Swift apps. The Swift standard library provides the AsyncImage component providing a caching implementation using URLCache. If you need more advanced image processing solutions, we can reach out for 3rd party libraries, but we have to watch out not to add potential risks to our project.

If you like to improve your SwiftUI knowledge, even more, check out the SwiftUI category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.

Thanks!

 
Antoine van der Lee

Written by

Antoine van der Lee

iOS Developer since 2010, former Staff iOS Engineer at WeTransfer and currently full-time Indie Developer & Founder at SwiftLee. Writing a new blog post every week related to Swift, iOS and Xcode. Regular speaker and workshop host.

Are you ready to

Turn your side projects into independence?

Learn my proven steps to transform your passion into profit.