How to use ‘AsyncImage’ in swiftUI?

By | May 27, 2024

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!

Leave a Reply

Your email address will not be published. Required fields are marked *