AsyncImage
is a component introduced in SwiftUI to handle the loading and displaying of remote images asynchronously. It simplifies the process of fetching images from the web, handling the loading state, and displaying a placeholder until the image is ready. Here’s a detailed breakdown of how AsyncImage
works, its customisation options, and usage examples.
Basic Usage
To use AsyncImage
, you need to provide a URL pointing to the image resource. The simplest form of AsyncImage
looks like this:
import SwiftUI
struct ContentView: View {
var body: some View {
AsyncImage(url: URL(string: "https://sampleImage.com/image.jpg"))
}
}
Placeholder and Error Handling
AsyncImage
allows you to specify a placeholder that is displayed while the image is loading. You can also handle the error state if the image fails to load.
Placeholder:
To provide a placeholder, you use the placeholder
closure:
import SwiftUI
struct ContentView: View {
var body: some View {
AsyncImage(url: URL(string: "https://sampleImage.com/image.jpg")) { phase in
if let image = phase.image {
// Image loaded successfully
image
.resizable()
.scaledToFit()
} else if phase.error != nil {
// An error occurred while loading the image
Text("Failed to load image")
} else {
// Image is in the process of loading
ProgressView()
}
}
.frame(width: 100, height: 100)
}
}
Using AsyncImagePhase
The AsyncImage
initializer with the phase
parameter provides greater control over the image loading states. The phase
is an enumeration with three cases:
empty
: No image is loaded yet.success(Image)
: The image was successfully loaded.failure(Error)
: The image failed to load.
Here’s how you can use it:
import SwiftUI
struct ContentView: View {
var body: some View {
AsyncImage(url: URL(string: "https://sampleImage.com/image.jpg")) { phase in
switch phase {
case .empty:
ProgressView()
.frame(width: 100, height: 100)
case .success(let image):
image
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
case .failure:
Image(systemName: "exclamationmark.triangle")
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.foregroundColor(.red)
@unknown default:
EmptyView()
}
}
}
}
Customizing the Image Loading
You can also customise the way images are fetched and processed by providing a URLSession
or custom caching logic if needed. Here’s an example of using a custom configuration:
import SwiftUI
struct ContentView: View {
let imageLoader: ImageLoader = ImageLoader()
var body: some View {
AsyncImage(url: URL(string: "https://sampleImage.com/image.jpg"),
scale: 1.0,
transaction: .init(animation: .default)) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
.resizable()
.scaledToFit()
case .failure:
Image(systemName: "exclamationmark.triangle")
.resizable()
.scaledToFit()
.foregroundColor(.red)
@unknown default:
EmptyView()
}
}
}
}
class ImageLoader: ObservableObject {
// Custom image loading logic
}
Advanced Usage with Custom Caching
For more advanced use cases, you might need to implement custom caching logic. While AsyncImage
itself does not provide built-in caching, you can integrate it with a custom image caching mechanism using Combine
or third-party libraries.
Here’s a simplified example of custom caching:
import SwiftUI
import Combine
class ImageCache {
static let shared = ImageCache()
private var cache: [URL: UIImage] = [:]
func image(for url: URL) -> UIImage? {
return cache[url]
}
func setImage(_ image: UIImage, for url: URL) {
cache[url] = image
}
}
class ImageLoader: ObservableObject {
@Published var image: UIImage?
private var cancellable: AnyCancellable?
func loadImage(from url: URL) {
if let cachedImage = ImageCache.shared.image(for: url) {
self.image = cachedImage
return
}
cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.receive(on: DispatchQueue.main)
.sink { [weak self] image in
self?.image = image
if let image = image {
ImageCache.shared.setImage(image, for: url)
}
}
}
}
struct AsyncImageView: View {
@StateObject private var loader = ImageLoader()
let url: URL
var body: some View {
if let image = loader.image {
Image(uiImage: image)
.resizable()
.scaledToFit()
} else {
ProgressView()
.onAppear { loader.loadImage(from: url) }
}
}
}
This example demonstrates creating a simple image caching system and integrating it with a custom AsyncImageView
.
AsyncImage
in SwiftUI provides a convenient and efficient way to handle remote image loading, offering customisation options for different loading states, and allowing integration with custom image loading and caching mechanisms. This makes it a versatile tool for managing image loading in your SwiftUI applications.
Hope you enjoyed reading and it helped!