Cheat Sheet v4 - aiXpertLab/OpenAI-ChatGPT-Practices GitHub Wiki

Feel free to copy & paste code below.

This documentation will describe some most common usage of Kingfisher. The code snippet is based on iOS. However, the similar code should also work for other platforms like macOS, by replacing the corresponding class (such as UIImage to NSImage, etc).

Working with Kingfisher extensions for UI component

Some convenient extensions are supplied in Kingfisher. It covers most used features, and supports all regular image related UI components: UIImageView, NSImageView, UIButton and NSButton. We recommend to use these methods to keep your code simple.

Simply set an image from url to an image view

let url = URL(string: "https://domain.com/image.jpg")!
imageView.kf.setImage(with: url)

Kingfisher will try to get the image from cache first. If not found, download and cache it for later use.

The absoluteString of url will be used as the cache key by default.

Use a specified key other than the url for cache

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

With a placeholder image while downloading

let image = UIImage(named: "default_profile_icon")
imageView.kf.setImage(with: url, placeholder: image)

You could also use a customized UIView or NSView as placeholder. Just make the view type conforming to Placeholder:

class MyView: UIView { /* Your implementation of view */ }
	
extension MyView: Placeholder { /* Just leave it empty */}
	
imageView.kf.setImage(with: url, placeholder: MyView())

The MyView instance will be added to / removed from the imageView as needed.

With a completion handler

imageView.kf.setImage(with: url, completionHandler: { 
    (image, error, cacheType, imageUrl) in
    // image: Image? `nil` means failed
    // error: NSError? non-`nil` means failed
    // cacheType: CacheType
    //                  .none - Just downloaded
    //                  .memory - Got from memory cache
    //                  .disk - Got from disk cache
    // imageUrl: URL of the image
})

With a loading indicator while downloading

imageView.kf.indicatorType = .activity
imageView.kf.setImage(with: url)

Use your own GIF file or any image as the indicator image while downloading

let p = Bundle.main.path(forResource: "loader", ofType: "gif")!
let data = try! Data(contentsOf: URL(fileURLWithPath: p))

imageView.kf.indicatorType = .image(imageData: data)

imageView.kf.setImage(with: url)

Customize the indicator with any view you want

struct MyIndicator: Indicator {
    let view: UIView = UIView()
    
    func startAnimatingView() { view.isHidden = false }
    func stopAnimatingView() { view.isHidden = true }
    
    init() {
        view.backgroundColor = .red
    }
}

let i = MyIndicator()
imageView.kf.indicatorType = .custom(indicator: i)

Or update your own indicator UI with progress block

imageView.kf.setImage(with: url, progressBlock: {
    receivedSize, totalSize in
    let percentage = (Float(receivedSize) / Float(totalSize)) * 100.0
    print("downloading progress: \(percentage)%")
    myIndicator.percentage = percentage
})

Add a fade transition when setting image after downloaded

imageView.kf.setImage(with: url, options: [.transition(.fade(0.2))])

Transform downloaded image to round corner before displaying and caching

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

You can also use other processor to get blur/tint/black & white or some other effect.

Apply multiple processor before setting the image

let processor = BlurImageProcessor(blurRadius: 4) >> RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: url, placeholder: nil, options: [.processor(processor)])

Skip cache searching, force downloading image again

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

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

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

For UIButton & NSButton

let uiButton: UIButton = //...
uiButton.kf.setImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
uiButton.kf.setBackgroundImage(with: url, for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)

let nsButton: NSButton = //...
nsButton.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
nsButton.kf.setAlternateImage(with: url, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)

Cache & Downloader

Kingfisher is composed with two main component: an ImageDownloader for downloading images, and an ImageCache to manipulate the cache. You can use either of them separately.

Use ImageDownloader to download an image without caching

ImageDownloader.default.downloadImage(with: url, options: [], progressBlock: nil) {
    (image, error, url, data) in
    print("Downloaded Image: \(image)")
}

Use ImageCache to store or get images

let image: UIImage = //...
ImageCache.default.store(image, forKey: "key_for_image")

let anotherImage: UIImage = //...
let imageData = //.. Data from anotherImage
ImageCache.default.store(anotherImage, 
                         original: imageData, 
                         forKey: "key_for_another_image", 
                         toDisk: false)

ImageCache.default.isImageCached(forKey: "key_for_image")
// (cached: true, cacheType: .memory)
ImageCache.default.isImageCached(forKey: "key_for_another_image")
// (cached: true, cacheType: .memory)

// Force quit and relaunch
ImageCache.default.isImageCached(forKey: "key_for_image")
// (cached: true, cacheType: .disk)
ImageCache.default.isImageCached(forKey: "key_for_another_image")
// (cached: false, cacheType: .none)

ImageCache.default.retrieveImage(forKey: "key_for_image", options: nil) { 
    image, cacheType in
    if let image = image {
	    print("Get image \(image), cacheType: \(cacheType).")
	    //In this code snippet, the `cacheType` is .disk
    } else {
       print("Not exist in cache.")
    }
}

Remove a cached image

// From both memory and disk
ImageCache.default.removeImage(forKey: "key_for_image")

// Only from memory
ImageCache.default.removeImage(forKey: "key_for_image", fromDisk: false)

Set maximum disk cache size for default cache

// 50 MB
ImageCache.default.maxDiskCacheSize = 50 * 1024 * 1024
// Default value is 0, which means no limit.

The default downloader will be used by image extension methods, if no customized one set.

Get used size of disk for a cache

ImageCache.default.calculateDiskCacheSize { size in
    print("Used disk size by bytes: \(size)")
}

Clear cache manually

// Clear memory cache right away.
cache.clearMemoryCache()

// Clear disk cache. This is an async operation.
cache.clearDiskCache()

// Clean expired or size exceeded disk cache. This is an async operation.
cache.cleanExpiredDiskCache()

Kingfisher will purge the memory cache when received a memory warning, as well as clean the expired and size exceeded cache when needed. Normally there is no need to clean cache yourself. These methods exist in case of you want your users have more control on the cache.

Set longest time duration of the cache being stored in disk

// 3 days
ImageCache.default.maxCachePeriodInSecond = 60 * 60 * 24 * 3
// Default value is 60 * 60 * 24 * 7, which means 1 week.

Set this value to a negative value (like -1) will make the disk cache never expiring.

Add a default path extension to each cached file

// Set a default path extension
KingfisherManager.shared.cache.pathExtension = "jpg"

Especially useful when using Kingfisher on macOS and wanting to add drag-drop. Most web input fields won't accept files without a path extension (like .jpg).

Set timeout duration for default image downloader

// 30 second
ImageDownloader.default.downloadTimeout = 30.0
// Default value is 15.

Use customized downloader and cache instead of the default ones

let downloader = ImageDownloader(name: "huge_image_downloader")
downloader.downloadTimeout = 150.0
let cache = ImageCache(name: "longer_cache")
cache.maxDiskCacheSize = 60 * 60 * 24 * 30

imageView.kf.setImage(with: url, options: [.downloader(downloader), .targetCache(cache)])

Cancel a downloading or retriving task

// In table view data source
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //...
    cell.imageView.kf.setImage(with: url)
    //...
}

// In table view delegate
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
	//...
	cell.imageView.kf.cancelDownloadTask()
}

Modify a request before sending

let modifier = AnyModifier { request in
    var r = request
    r.setValue("", forHTTPHeaderField: "Access-Token")
    return r
}       
imageView.kf.setImage(with: url, placeholder: nil, options: [.requestModifier(modifier)])

Authentication with NSURLCredential

// In ViewController
ImageDownloader.default.authenticationChallengeResponder = self

extension ViewController: AuthenticationChallengeResponsable {
    func downloader(_ downloader: ImageDownloader,
            didReceive challenge: URLAuthenticationChallenge,
               completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
    {
        // Provide your `AuthChallengeDisposition` and `URLCredential`
        let disposition: URLSession.AuthChallengeDisposition = // ..
        let credential: URLCredential? = //..
        completionHandler(disposition, credential)
    }
}

Check HTTP status code and determine whether it is valid

// In ViewController
ImageDownloader.default.delegate = self

extension ViewController: ImageDownloaderDelegate {
    func isValidStatusCode(_ code: Int, for downloader: ImageDownloader) -> Bool {
        return code == 200 || code == 202
    }
}

An invalidStatusCode error will be raised if false is returned in this delegate method.

Use your own session configuration in a downloader

let imageDownloader: ImageDownloader = //...

// A configuration without persistent storage for caches is 
// requsted for downloader working correctly. (Based on `URLSessionConfiguration.ephemeral`)
imageDownloader.sessionConfiguration = //...

Processor

ImageProcessor is more likly a transformer of image. It convert some data to an image or an image to another. You can supply a processor to ImageDownloader. The downloader will apply it to downloaded data/images, then send the processed images to the image view or cache if needed.

Use the default processor

// Just without anything
imageView.kf.setImage(with: url)
// It equals to
imageView.kf.setImage(with: url, options: [.processor(DefaultImageProcessor.default)])

DefaultImageProcessor converts downloaded data to a corresponded image object. PNG, JPEG and GIF are supported by default.

Built-in processors of Kingfisher

// Round corner
let processor = RoundCornerImageProcessor(cornerRadius: 20)

// Resizing
let processor = ResizingImageProcessor(referenceSize: CGSize(width: 100, height: 100))

// Cropping
let processor = CroppingImageProcessor(size: CGSize(width: 100, height: 100), anchor: CGPoint(x: 0.5, y: 0.5))

// Blur with a radius
let processor = BlurImageProcessor(blurRadius: 5.0)

// Overlay with a color & fraction
let processor = OverlayImageProcessor(overlay: .red, fraction: 0.7)

// Tint with a color
let processor = TintImageProcessor(tint: .blue)

// Adjust color
let processor = ColorControlsProcessor(brightness: 1.0, contrast: 0.7, saturation: 1.1, inputEV: 0.7)

// Black & White
let processor = BlackWhiteProcessor()

// Blend (iOS)
let processor = BlendImageProcessor(blendMode: .darken, alpha: 1.0, backgroundColor: .lightGray)

// Compositing
let processor = CompositingImageProcessor(compositingOperation: .darken, alpha: 1.0, backgroundColor: .lightGray)

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

Concatenating processors

// Blur and then make round corner
let processor = BlurImageProcessor(blurRadius: 5.0) >> RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: url, options: [.processor(processor)])

// `>>` equals the `append(another:)` method of `ImageProcessor`.
// Above equals to:
let processor = BlurImageProcessor(blurRadius: 5.0).append(RoundCornerImageProcessor(cornerRadius: 20))

Create and use your own processor

Make a type adopting to ImageProcessor and implement identifier and process:

struct WebpProcessor: ImageProcessor {
    // `identifier` should be the same for processors with same properties/functionality
    // It will be used when storing and retrieving the image to/from cache.
    let identifier = "com.yourdomain.webpprocessor"
    
    // Convert input data/image to target image and return it.
    func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
        switch item {
        case .image(let image):
            print("already an image")
            return image
        case .data(let data):
            return WebpFramework.createImage(from: webpData)
        }
    }
}

// Then pass it to the `setImage` methods:
let processor = WebpProcessor()
let url = URL(string: "https://yourdomain.com/example.webp")
imageView.kf.setImage(with: url, options: [.processor(processor)])

Create a processor from CIFilter

If you have a prepared CIFilter, you can create a processor quickly from it.

struct MyCIFilter: CIImageProcessor {

    let identifier = "com.yourdomain.myCIFilter"
    
    let filter = Filter { input in
        guard let filter = CIFilter(name: "xxx") else { return nil }
        filter.setValue(input, forKey: kCIInputBackgroundImageKey)
        return filter.outputImage
    }
}

Use processor with ImageCache

Kingfisher is smart enough to cache and get the processed images if you specified the correct ImageProcessor in option.

Although the process method of a ImageProcessor is used to convert data or original image to another image. The identifier will be used when caching the processed images.

Without the identifier, Kingfisher will not be able to tell which is the correct image in cache (Think about you have to store two version of an image from the same url, one should be round cornered and another should be blurred. You need two different cache keys).

When you use the extension methods of Kingfisher, please make sure to pass the ImageProcessor with options correctly, and Kingfisher will handle other things for you. However, there might be a chance that you need to interact with ImageCache yourself, for examle, to check whether a processed image already cached or not. In such situation, besides of passing in the key, you also need to supply the identifier:

let processor = WebpProcessor()
let url = URL(string: "https://yourdomain.com/example.webp")
imageView.kf.setImage(with: url, options: [.processor(processor)])

// Later
ImageCache.default.isImageCached(
         forKey: url.cacheKey, 
         processorIdentifier: processor.identifier)

Cache original image while using a processor

If you are trying to process a same image with multiple processors, it may be worth to pass .cacheOriginalImage as an option. This will store the original downloaded image to cache as well:

let p = MyProcessor()
imageView.kf.setImage(with: url, options: [.processor(p), .cacheOriginalImage])

Kingfisher will now try to get the image at url, apply processor p on it, then store both original image and the processed one to cache.

When retrieving an image (by ImageView extension or KingfisherManager), Kingfisher will firstly check the version exactly matches input processor. If not found, Kingfisher will turn to get the original image and then apply processor on it (and of course store it to cache). By this mean, there is no need to access the Internet and it could bring a great performance. At last, if neither the processed image nor the original image could be found in cache, a downloading will be started and followed a processing phase.

Serializer

CacheSerializer will be used to convert some data to an image object for retrieving from disk cache and vice versa for storing to disk cache.

Use the default serializer

// Just without anything
imageView.kf.setImage(with: url)
// It equals to
imageView.kf.setImage(with: url, options: [.cacheSerializer(DefaultCacheSerializer.default)])

DefaultCacheSerializer converts cached data to a corresponded image object and vice versa. PNG, JPEG and GIF are supported by default.

Format Indicated Cache

If you need to specify what the format of image should be, use FormatIndicatedCacheSerializer. It provided serializers for all built-in supported format: FormatIndicatedCacheSerializer.png, FormatIndicatedCacheSerializer.jpeg and FormatIndicatedCacheSerializer.gif.

By default (DefaultCacheSerializer), Kingfisher will respect the input image format and try to keep it unchanged. However, there are some exceptions. A common case is that when you using a RoundCornerImageProcessor, you may always want the alpha channel. If your original image is jpg, you may want to set the png serializer instead:

let roundCorner = RoundCornerImageProcessor(cornerRadius: 20)
imageView.kf.setImage(with: url, 
    options: [.processor(roundCorner), 
              .cacheSerializer(FormatIndicatedCacheSerializer.png)]
)

Create and use your own serializer

Make a type adopting to CacheSerializer and implement data(with:original:) and image(with:options:):

struct WebpCacheSerializer: CacheSerializer {
    func data(with image: Image, original: Data?) -> Data? {
        return WebpFramework.webpData(of: image)
    }
    
    func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? {
        return WebpFramework.createImage(from: webpData)
    }
}

// Then pass it to the `setImage` methods:
let serializer = WebpCacheSerializer()
let url = URL(string: "https://yourdomain.com/example.webp")
imageView.kf.setImage(with: url, options: [.cacheSerializer(serializer)])

Prefetch

You could prefetch some images and cache them before you display them on the screen. This is useful when you know a list of image resources you know they would probably be shown later.

let urls = ["http://example.com/image1.jpg", "http://example.com/image2.jpg"]
           .map { URL(string: $0)! }
let prefetcher = ImagePrefetcher(urls: urls) {
    skippedResources, failedResources, completedResources in
    print("These resources are prefetched: \(completedResources)")
}
prefetcher.start()

// Later when you need to display these images:
imageView.kf.setImage(with: urls[0])
anotherImageView.kf.setImage(with: urls[1])

Use Prefetcher with UICollectionView or UITableView

In iOS 10, Apple introduced a cell prefetching behavior. It could work seamlessly with Kingfisher's ImagePrefetcher.

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView?.prefetchDataSource = self
}

extension ViewController: UICollectionViewDataSourcePrefetching {
    func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
        let urls = indexPaths.flatMap { URL(string: $0.urlString) }
        ImagePrefetcher(urls: urls).start()
    }
}

See WWDC 16 - Session 219 for more about changing of it in iOS 10.

Animated GIF

let imageView: UIImageView = ...
imageView.kf.setImage(with: URL(string: "your_animated_gif_image_url")!)

If you encountered to memory issues when dealing with large GIF, try to use AnimatedImageView instead of regular image view to display GIF. It will only decode several frames of your GIF image to get a smaller memory footprint (but high CPU load).

let imageView = AnimatedImageView()
imageView.kf.setImage(with: URL(string: "your_large_animated_gif_image_url")!)

Only load the first frame from GIF

imageView.kf.setImage(with: URL(string: "your_animated_gif_image_url")!, 
                   options: [.onlyLoadFirstFrame])

It will be useful when you just want to display a static preview of the first frame from a GIF image.

Useful image extensions

Some processor/filter code is exposed, that means you can apply it seperately on an image regardless where it comes from. They are listed as below.

extension Image {
    public func kf.image(withRoundRadius radius: CGFloat, fit size: CGSize, scale: CGFloat) -> Image { }
    public func kf.resize(to size: CGSize) -> Image { }
    public func kf.blurred(withRadius radius: CGFloat) -> Image { }
    public func kf.overlaying(with color: Color, fraction: CGFloat) -> Image { }
    public func kf.tinted(with color: Color) -> Image { }
    public func kf.adjusted(brightness: CGFloat, contrast: CGFloat, saturation: CGFloat, inputEV: CGFloat) -> Image { }
    public func kf.apply(_ filter: Filter) -> Image { }
}

Please also see the full API Reference to find out more.