Back to Kingfisher

Common Tasks - Cache

Sources/Documentation.docc/CommonTasks/CommonTasks_Cache.md

8.8.17.5 KB
Original Source

Common Tasks - Cache

Common tasks related to the ImageCache in Kingfisher.

Overview

Kingfisher employs a hybrid ImageCache for managing cached images, comprising both memory and disk storage. It offers high-level APIs for cache management. Unless otherwise specified, the ImageCache/default instance is used throughout Kingfisher.

Using another cache key

By default, the URL is converted into a string to generate the cache key. For network URLs, absoluteString is utilized. You can customize the key by creating an ImageResource object with a specified key.

swift
let resource = ImageResource(
    downloadURL: url, 
    cacheKey: "my_cache_key"
)
imageView.kf.setImage(with: resource)

Kingfisher uses the cacheKey to locate images in the cache. Ensure you use a distinct key for each different image.

Checking whether an image in the cache

swift
let cache = ImageCache.default
let cached = cache.isCached(forKey: cacheKey)

// To know where the cached image is:
let cacheType = cache.imageCachedType(forKey: cacheKey)
// `.memory`, `.disk` or `.none`.

Note: ImageCache/imageCachedType(forKey:processorIdentifier:forcedExtension:) may touch the disk cache synchronously when checking .disk. If you want to avoid blocking the calling thread (for example, in a scrolling UI on the main thread), use the opt-in async API instead:

swift
cache.imageCachedTypeAsync(forKey: cacheKey) { cacheType in
    // `.memory`, `.disk` or `.none`.
}

// Or with async/await:
let cacheType = await cache.imageCachedTypeAsync(forKey: cacheKey)

If a processor is applied when retrieving an image, the processed image will be cached. In this scenario, remember to also include the processor identifier when manipulating the cache:

swift
let processor = RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: url, options: [.processor(processor)])

// Later
cache.isCached(forKey: cacheKey, processorIdentifier: processor.identifier)

Getting an image from the cache

swift
cache.retrieveImage(forKey: "cacheKey") { result in
    switch result {
    case .success(let value):
        print(value.cacheType)

        // If the `cacheType is `.none`, `image` will be `nil`.
        print(value.image)

    case .failure(let error):
        print(error)
    }
}

Set limit for the cache

For memory storage, you can set its MemoryStorage/Config/totalCostLimit and MemoryStorage/Config/countLimit:

swift
// Limit memory cache size to 300 MB.
cache.memoryStorage.config.totalCostLimit = 300 * 1024 * 1024

// Limit memory cache to hold 150 images at most. 
cache.memoryStorage.config.countLimit = 150

The default MemoryStorage/Config/totalCostLimit for the memory cache is set to 25% of the device's total memory, with no limit on the MemoryStorage/Config/countLimit.

For disk storage, you have the option to set a DiskStorage/Config/sizeLimit to manage the space used on the file system.

swift
// Limit disk cache size to 1 GB.
cache.diskStorage.config.sizeLimit = 1000 * 1024 * 1024

Set the default expiration for cache

Both memory and disk storage in Kingfisher have default expiration settings. Images in memory storage expire 5 minutes after the last access, whereas images in disk storage expire after one week. These values can be modified as follows:

swift
// Set memory image expires after 10 minutes.
cache.memoryStorage.config.expiration = .seconds(600)

// Set disk image never expires.
cache.diskStorage.config.expiration = .never

To override this default expiration for a specific image when caching it, include an option as follows during image setting:

swift
// This image will never expire in memory cache.
imageView.kf.setImage(with: url, options: [.memoryCacheExpiration(.never)])

The expired memory cache is purged every 2 minutes by default. To adjust this frequency:

swift
// Check memory clean up every 30 seconds.
cache.memoryStorage.config.cleanInterval = 30

Store images to cache manually

By default, view extension methods and KingfisherManager automatically store retrieved images in the cache. However, you can also manually store an image to the cache:

swift
let image: UIImage = //...
cache.store(image, forKey: cacheKey)

If you possess the original data of the image, pass it along to ImageCache. This assists Kingfisher in determining the appropriate format for storing the image:

swift
let data: Data = //...
let image: UIImage = //...
cache.store(image, original: data, forKey: cacheKey)

Remove images from cache manually

Kingfisher manages its cache automatically. But you still can manually remove a certain image from cache:

swift
cache.removeImage(forKey: cacheKey)

Or, with more control:

swift
cache.removeImage(
    forKey: cacheKey,
    processorIdentifier: processor.identifier,
    fromMemory: false,
    fromDisk: true)
{
    print("Removed!")
}

Clear the cache

swift
// Remove all.
cache.clearMemoryCache()
cache.clearDiskCache { print("Done") }

// Remove only expired.
cache.cleanExpiredMemoryCache()
cache.cleanExpiredDiskCache { print("Done") }

Report the disk storage size

swift
ImageCache.default.calculateDiskStorageSize { result in
    switch result {
    case .success(let size):
        print("Disk cache size: \(Double(size) / 1024 / 1024) MB")
    case .failure(let error):
        print(error)
    }
}

Create your own cache and use it

swift
// The `name` parameter is used to identify the disk cache bound to the `ImageCache`.
let cache = ImageCache(name: "my-own-cache")
imageView.kf.setImage(with: url, options: [.targetCache(cache)])

Skipping cache searching, force downloading image again

swift
imageView.kf.setImage(with: url, options: [.forceRefresh])

Only search cache for the image, do not download if not existing

This makes your app to an "offline" mode.

swift
imageView.kf.setImage(with: url, options: [.onlyFromCache])

If the image does not exist in the cache, an KingfisherError/CacheErrorReason/imageNotExisting(key:) error will be triggered.

Waiting for cache to finish

Storing images in the disk cache is asynchronous and doesn't need to be completed before setting the image view and invoking the completion handler in view extension methods. This means that the disk cache might not be fully updated at the time the completion handler is executed, as shown below:

swift
imageView.kf.setImage(with: url) { _ in
    ImageCache.default.retrieveImageInDiskCache(forKey: url.cacheKey) { result in
        switch result {
        case .success(let image):
            // `image` might be `nil` here.
        case .failure: break
        }
    }
}

For most scenarios, this asynchronous behavior isn't an issue. However, if your logic relies on the existence of the disk cache, use the .waitForCache option. With this option, Kingfisher will delay the execution of the handler until the disk cache operation is complete:

swift
imageView.kf.setImage(with: url, options: [.waitForCache]) { _ in
    ImageCache.default.retrieveImageInDiskCache(forKey: url.cacheKey) { result in
        switch result {
        case .success(let image):
            // `image` exists.
        case .failure: break
        }
    }
}

This consideration applies specifically to disk image caching, which involves asynchronous I/O operations. In contrast, memory cache operations are synchronous, ensuring that the image is always available in the memory cache.