Is this a good way to download images from json Api? In my SwiftUI app, I'm trying to get some data from a json Api and display this data along with an image. First I have a function that fetches the data from the Api, then I have another function that downloads the image data. The image takes a while to appear(1-2) seconds.
I'm wondering if this is the best way to do it, especially if I'm downloading multiple images to display them in a List or a Grid.
func fetchAPI() {

    URLSession.shared.dataTask(with: url) { (data, response, error) in

/* Code that decodes the json and gets the imageURL string */

            DispatchQueue.main.async {

                self.downloadImage(url: imageURL) // call the function that downloads the imageData
            }
        } catch {
            print(&quot;Error: \(error.localizedDescription)&quot;)
        }
    }
    .resume()
} 

The function that downloads the image:
func downloadImage(url: String) {

    guard let imageURL = URL(string: url) else {
        print(&quot;Error: Invalid image URL&quot;)
        return
    }

    URLSession.shared.dataTask(with: imageURL) { data, response, error in
        guard let recievedData = data, error == nil  else {
            print(&quot;Error: No Data&quot;)
            return
        }

        DispatchQueue.main.async {
            self.imageData = recievedData 
        }
    }
    .resume()
}
Maybe the image is in question is a little bit large or the internet connection used is a little bit slow.
I personally would go about it differently.
For UIKit
import UIKit
import Foundation

extension UIImageView {
    func load(url: URL) {
        DispatchQueue.global().async { [weak self] in
            if let data = try? Data(contentsOf: url) {
                if let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        self?.image = image
                    }
                }
            }
        }
    }
}

For SwiftUI we have to go about it a little bit differently but there are two ways
The very simple one-time usage:
import Foundation
import SwiftUI
import Combine

// Image Loader
class ImageLoader: ObservableObject { 
    var dataPublisher = PassthroughSubject&lt;Data, Never&gt;() 
    var data = Data() { 
        didSet { 
            dataPublisher.send(data) 
        } 
     }

     init(urlString:String) { 
        guard let url = URL(string: urlString) else { return } 
        let task = URLSession.shared.dataTask(with: url) { data, response, error in 
            guard let data = data else { return }         
            DispatchQueue.main.async { 
                self.data = data 
            } 
        } 
        task.resume() 
     }
}

// Image View
struct ImageView: View {
    @ObservedObject var imageLoader:ImageLoader
    @State var image:UIImage = UIImage()

    init(withURL url:String) {
        imageLoader = ImageLoader(urlString: url)
    }

    var body: some View {
        VStack { 
            Image(uiImage: image)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width:100, height:100)
        }.onReceive(imageLoader.dataPublisher) { data in
            self.image = UIImage(data: data) ?? UIImage()
        }
    }
}

struct ImageView_Previews: PreviewProvider {
    static var previews: some View {
        ImageView(withURL: &quot;&quot;)
    }
}

The more advanced way
import Foundation
import SwiftUI
import Combine

// ImageCache
protocol ImageCache {
    subscript(_ url: URL) -&gt; UIImage? { get set }
}

struct TemporaryImageCache: ImageCache {
    private let cache: NSCache&lt;NSURL, UIImage&gt; = {
        let cache = NSCache&lt;NSURL, UIImage&gt;()
        cache.countLimit = 100 // 100 items
        cache.totalCostLimit = 1024 * 1024 * 100 // 100 MB
        return cache
    }()
    
    subscript(_ key: URL) -&gt; UIImage? {
        get {
            self.cache.object(forKey: key as NSURL)
        }
        set {
            newValue == nil ? self.cache.removeObject(forKey: key as NSURL) : self.cache.setObject(newValue!, forKey: key as NSURL)
        }
    }
}

// EnvironmentValues+ImageCache
struct ImageCacheKey: EnvironmentKey {
    static let defaultValue: ImageCache = TemporaryImageCache()
}

extension EnvironmentValues {
    var imageCache: ImageCache {
        get {
            self[ImageCacheKey.self]
        }
        set {
            self[ImageCacheKey.self] = newValue
        }
    }
}

// Image Loader
class ImageLoader: ObservableObject {
    @Published
    var image: UIImage?
    
    private(set) var isLoading = false
    
    private let url: URL
    private var cache: ImageCache?
    private var cancellable: AnyCancellable?
    
    private static let imageDownloadingQueue = DispatchQueue(label: &quot;image-downloading&quot;)
    
    init(url: URL, cache: ImageCache? = nil) {
        self.url = url
        self.cache = cache
    }
    
    deinit {
        self.cancel()
    }
    
    func load() {
        guard !self.isLoading else { return }
        
        if let image = self.cache?[self.url] {
            self.image = image
            return
        }
        
        self.cancellable = URLSession.shared.dataTaskPublisher(for: self.url)
            .map {
                UIImage(data: $0.data)
            }
            .replaceError(with: nil)
            .handleEvents(receiveSubscription: { [weak self] _ in
                self?.onStart()
            },
            receiveOutput: { [weak self] in
                self?.cache($0)
            },
            receiveCompletion: { [weak self] _ in
                self?.onFinish()
            },
            receiveCancel: { [weak self] in
                self?.onFinish()
            })
            .subscribe(on: Self.imageDownloadingQueue)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] in
                self?.image = $0
            }
    }
    
    func cancel() {
        self.cancellable?.cancel()
    }
    
    private func onStart() {
        self.isLoading = true
    }
    
    private func onFinish() {
        self.isLoading = false
    }
    
    private func cache(_ image: UIImage?) {
        image.map {
            self.cache?[self.url] = $0
        }
    }
}

// Image View
struct ImageView&lt;Placeholder: View&gt;: View {
    @StateObject
    private var loader: ImageLoader
    private let placeholder: Placeholder
    private let image: (UIImage) -&gt; Image
    
    init(url: URL, @ViewBuilder placeholder: () -&gt; Placeholder, @ViewBuilder image: @escaping (UIImage) -&gt; Image = Image.init(uiImage:)) {
        self.placeholder = placeholder()
        self.image = image
        self._loader = StateObject(wrappedValue: ImageLoader(url: url, cache: Environment(\.imageCache).wrappedValue))
    }
        
    var body: some View {
        self.content.onAppear(perform: loader.load)
    }
    
    private var content: some View {
        Group {
            if let image = self.loader.image {
                self.image(image)
            } else {
                self.placeholder
            }
        }
    }
}

```