Bad practice: loading a large image on the main thread
You’re more of a video kind of person? I’ve got you covered! Here’s a video with the same content than this article 🍿
Can you guess what’s the problem with this code?
We’re creating a UIImage
and then we assign it to a UIImageView
.
At first glance, there’s nothing special: you’ve probably written similar code dozens of time.
However, you can notice that I’m hinting at the fact that the image is quite large:
That’s a problem because loading a large image is an operation that takes some time to complete.
And with the way the code is currently written, this loading operation will take place on the main thread.
This means that the main thread will be blocked while the image loads…
So if this code is, for example, part of the configuration of a UICollectionViewCell
, it could result in a scrolling animation that looks laggy and uncomfortable to the eye.
Of course, this is something that we definitely want to avoid!
The typical solution would be to offload the heavy work to a background thread.
But that’s not possible here: because UIImageView
is a UIKit
object, and so interacting with it from a background thread would result in a crash.
And that’s when the method prepareForDisplay
can help you!
When we call this method on a UIImage
, it will decode the image on a background thread, and then call its completionHandler
once the decoded image is ready to be displayed 👌
Just don’t forget to switch back to the main thread before you set the preparedImage
on the imageView
!
As you can see, the method prepareForDisplay
is very helpful and is not that hard to integrate.
And if you’re using Swift Concurrency, it will be even easier, because all you’ll need to do is call its async
variant and the thread switching will be handled automatically!
That’s all for this article, I hope you’ve enjoyed discovering this recent API!
Here’s the code if you want to experiment with it:
import UIKit
// Before
let image = UIImage(named: "big-image")
imageView.image = image
// After
let image = UIImage(named: "big-image")
image?.prepareForDisplay { [weak self] preparedImage in
DispatchQueue.main.async {
self?.imageView.image = preparedImage
}
}
// using Swift Concurrency
let image = UIImage(named: "big-image")
Task {
imageView.image = await image?.byPreparingForDisplay()
}