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!