iOS: Kingfisher, Nuke and BlurHash Placeholders
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. See the section bibliography for the reference material consulted while preparing these pages. |
Baseline date: 2026-10-07. Check each library’s README for its current version and minimum iOS version before adopting it, since the SwiftUI APIs below have changed between major releases. The platform’s own capture, photo library and video APIs are in Camera, Media and Photos. This page is about loading, caching and placeholder rendering of remote images from a media backend, see responsive images and thumbnails.
AsyncImage and what it does not do
AsyncImage downloads a URL and shows a placeholder, then
the image, optionally with a custom phase handler and a transaction animation.
AsyncImage(url: coverURL) { phase in
switch phase {
case .success(let image): image.resizable().scaledToFill()
case .failure: Image(systemName: "photo")
default: ProgressView()
}
}
.frame(width: 160, height: 240)
.clipped()
It has no image cache of its own beyond the shared URLCache, so scrolling back through a list can download images again.
It offers no prefetching, no downsampling (a large file is decoded at full size, which costs memory), no control over
the cache or the request, and no support for a BlurHash placeholder beyond what you place in the closure. Use it for a
handful of small images. For lists and grids, use a library, or build on URLSession and ImageIO yourself.
Kingfisher
Kingfisher provides a memory and disk cache, processors and SwiftUI views.
import Kingfisher
import SwiftUI
struct CoverView: View {
let url: URL
let blurhash: String?
var body: some View {
KFImage(url)
.placeholder { BlurHashPlaceholder(hash: blurhash) }
.setProcessor(DownsamplingImageProcessor(size: CGSize(width: 160, height: 240)))
.scaleFactor(UIScreen.main.scale)
.cacheOriginalImage()
.fade(duration: 0.25)
.resizable()
.scaledToFill()
.frame(width: 160, height: 240)
.clipped()
}
}
DownsamplingImageProcessor uses ImageIO to decode the file straight to the target size, so a 1280 pixel image is never fully
decoded for a 160 point slot. scaleFactor makes the target size pixel-accurate on Retina screens. Prefetch the next
page of covers with ImagePrefetcher(urls:).start(). Reference: Kingfisher
cheat sheet and the API documentation.
Nuke
Nuke separates the pipeline (ImagePipeline), the SwiftUI view (LazyImage in the NukeUI
module) and prefetching (ImagePrefetcher).
import Nuke
import NukeUI
import SwiftUI
struct CoverView: View {
let url: URL
let blurhash: String?
var body: some View {
LazyImage(request: ImageRequest(url: url, processors: [.resize(width: 160)])) { state in // points; Nuke applies the screen scale
if let image = state.image {
image.resizable().scaledToFill()
} else {
BlurHashPlaceholder(hash: blurhash)
}
}
.frame(width: 160, height: 240)
.clipped()
}
}
let prefetcher = ImagePrefetcher()
prefetcher.startPrefetching(with: nextPageURLs)
Nuke resizes in the pipeline, caches the decoded and the original data, and coalesces duplicate requests. Configure a larger
cache with ImagePipeline.Configuration (a DataCache for the disk), and share one pipeline. Reference:
Nuke documentation and the
README.
SDWebImage
SDWebImage is the long-established alternative, with an Objective-C core, the
SDWebImageSwiftUI package for SwiftUI (WebImage(url:)), and coder plugins for formats such as WebP and AVIF (for
older iOS versions that lack system support). Choose it if you already use it, or you need its coder plugins.
| Library | Choose it when |
|---|---|
|
Few small images, no caching needs. |
Kingfisher |
You want a mature, easy API with processors and a SwiftUI view. |
Nuke |
You want a pipeline you can configure and prefetch, and a small, modern API. |
SDWebImage |
You have existing usage, or need its extra codecs. |
Formats on Apple platforms
iOS and macOS decode JPEG, PNG, GIF, HEIC and, from iOS 14, WebP through ImageIO. AVIF decoding was added in iOS 16.
Check the ImageIO documentation for the exact versions and ask the API for
the format the device supports, as in the bestVariant helper of
the Android page, using #available(iOS 16, *).
BlurHash with the woltapp Swift implementation
The Swift implementation in the reference repository is meant to be
copied into the project. It adds an initialiser to UIImage:
struct BlurHashPlaceholder: View {
let hash: String?
var body: some View {
if let hash, let image = UIImage(blurHash: hash, size: CGSize(width: 32, height: 32), punch: 1) {
Image(uiImage: image).resizable().scaledToFill()
} else {
Color.gray.opacity(0.2)
}
}
}
Decode at 32 by 32 points and let SwiftUI scale it. Cache the decoded UIImage per hash (an NSCache), because the view
body can run many times, and never decode in body for a long list without that cache. The algorithm is in
the BlurHash page. The package also has an encoder, for when the
app computes the hash before an upload, see
device-side compression and uploads.