Image caching with NSCache and async loading

Sofia Martinez Jan 2026
2 tabs
import UIKit

class ImageCache {
    static let shared = ImageCache()

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

    private init() {
        cache.countLimit = 100
        cache.totalCostLimit = 50 * 1024 * 1024 // 50 MB
    }

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

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

    func remove(forKey key: String) {
        cache.removeObject(forKey: key as NSString)
    }

    func removeAll() {
        cache.removeAllObjects()
    }
}

class ImageLoader: ObservableObject {
    @Published var image: UIImage?
    @Published var isLoading = false

    private let url: URL
    private let cache = ImageCache.shared

    init(url: URL) {
        self.url = url
    }

    @MainActor
    func load() async {
        let urlString = url.absoluteString

        // Check cache first
        if let cached = cache.get(forKey: urlString) {
            image = cached
            return
        }

        isLoading = true

        do {
            let (data, _) = try await URLSession.shared.data(from: url)

            if let downloadedImage = UIImage(data: data) {
                cache.set(downloadedImage, forKey: urlString)
                image = downloadedImage
            }
        } catch {
            print("Failed to load image: \(error)")
        }

        isLoading = false
    }
}
2 files · swift Explain with highlit

Loading images from URLs requires caching to avoid redundant network calls and improve performance. I create an image cache using NSCache which automatically evicts objects under memory pressure. The cache stores UIImage or Data keyed by URL. For async loading, I use URLSession.shared.data(from:) with async/await. SwiftUI's AsyncImage handles loading states but doesn't cache—I build a custom CachedAsyncImage view that checks cache before loading. Prefetching images for list items improves perceived performance. For complex image pipelines with transforms and placeholders, libraries like Kingfisher or SDWebImage help, but a simple cache covers most needs.