Image Caching in SwiftUI

Image Caching in SwiftUI

·

3 min read

Caching images is an important technique for optimizing the performance of your SwiftUI app. When you load an image, it can take time to fetch it from a remote server or read it from disk. By caching the image, you can avoid repeating this time-consuming process every time the image is needed. In this blog post, we'll explore how to cache images in SwiftUI.

The code in this post is available here.

Using Image Cache

One way to cache images in SwiftUI is to create an ImageCache class. This class can store a cache of UIImage objects, and it can be accessed from any part of your app. Here's an example of how you can create an ImageCache class:

class ImageCache {
    static let shared = ImageCache()

    private let cache = NSCache<NSString, UIImage>()

    private init() {}

    func set(_ image: UIImage, forKey key: String) {
        cache.setObject(image, forKey: key as NSString)
    }

    func get(forKey key: String) -> UIImage? {
        return cache.object(forKey: key as NSString)
    }
}

In this example, we've created a singleton ImageCache object using the shared property. The cache property is an NSCache object that stores UIImage objects, and we've defined set(_:,forKey:) and get(forKey:) methods to add and retrieve images from the cache.

Caching Images with URL

Another common use case for caching images in SwiftUI is when loading images from a remote server. In this case, you can use the dataTask(with:completionHandler:) method of the URLSession API to load the image data asynchronously. Once the image data is loaded, you can cache it using the ImageCache class.

struct RemoteImage: View {
    @ObservedObject var imageLoader: ImageLoader

    init(url: String) {
        imageLoader = ImageLoader(url: url)
    }

    var body: some View {
        if let image = imageLoader.image {
            Image(uiImage: image)
                .resizable()
        } else {
            ProgressView()
        }
    }
}

class ImageLoader: ObservableObject {
    @Published var image: UIImage?

    private var url: String
    private var task: URLSessionDataTask?

    init(url: String) {
        self.url = url
        loadImage()
    }

    private func loadImage() {
        if let cachedImage = ImageCache.shared.get(forKey: url) {
            self.image = cachedImage
            return
        }

        guard let url = URL(string: url) else { return }

        task = URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else { return }

            DispatchQueue.main.async {
                let image = UIImage(data: data)
                self.image = image
                ImageCache.shared.set(image!, forKey: self.url)
            }
        }
        task?.resume()
    }
}

In this example, we've created a RemoteImage view that loads an image from a remote server using the ImageLoader class. The ImageLoader class is an ObservableObject that uses the dataTask(with:completionHandler:) method to load the image data asynchronously. We've also added a check for cached images using the ImageCache class, which avoids fetching the image from the remote server if it's already cached.

Note: To validate the implementation above, you can use the image from the project iOSDevX with the URL: "https://github.com/xavier7t/iOSDevX/blob/main/iOSDevX/Assets.xcassets/demo.imageset/demo.png?raw=true". You can write a simple SwiftUI View like the one below:

struct ContentView: View {
    let urlString = "https://github.com/xavier7t/iOSDevX/blob/main/iOSDevX/Assets.xcassets/demo.imageset/demo.png?raw=true"
    var body: some View {
        VStack {
            RemoteImage(url: urlString)
                .frame(width: 150, height: 150, alignment: .center)
                .cornerRadius(10)
        }
    }
}

Conclusion

Caching images is an important technique for optimizing the performance of your SwiftUI app. By creating an ImageCache class and using it to cache images, you can avoid repeating time-consuming image-loading operations.

And that’s all of today’s post. I hope it helps and let me know if it is by leaving a comment. Don’t forget to subscribe to my newsletter if you’d like to receive posts like this via email.

I’ll see you in the next post!