title: How can I load an UIImage into a SwiftUI Image asynchronously?
body: <p>In SwiftUI there are some <code>.init</code> methods to create an Image but none of them admits a block or any other way to load an UIImage from network/cache... </p>
  
  <p>I am using <a href="https://github.com/onevcat/Kingfisher" rel="noreferrer">Kingfisher</a> to load images from network and cache inside a list row, but the way to draw the image in the view is to re-render it again, which I would prefer to not do. Also, I am creating a fake image(only coloured) as placeholder while the image gets fetched.
  Another way would be to wrap all inside a custom view and only re-render the wrapper. But I haven't tried yet.</p>
  
  <p>This sample is working right now.
  Any idea to improve the current one will be great</p>
  
  <p>Some view using the loader</p>
  
  <pre class="lang-swift prettyprint-override"><code>struct SampleView : View {
  
      @ObjectBinding let imageLoader: ImageLoader
  
      init(imageLoader: ImageLoader) {
          self.imageLoader = imageLoader
      }
  
      var body: some View {
         Image(uiImage: imageLoader.image(for: "https://url-for-image"))
            .frame(width: 128, height: 128)
            .aspectRatio(contentMode: ContentMode.fit)
      }
  
  }
  </code></pre>
  
  <pre class="lang-swift prettyprint-override"><code>import UIKit.UIImage
  import SwiftUI
  import Combine
  import class Kingfisher.ImageDownloader
  import struct Kingfisher.DownloadTask
  import class Kingfisher.ImageCache
  import class Kingfisher.KingfisherManager
  
  class ImageLoader: BindableObject {
  
      var didChange = PassthroughSubject&lt;ImageLoader, Never&gt;()
      private let downloader: ImageDownloader
      private let cache: ImageCache
      private var image: UIImage? {
          didSet {
              dispatchqueue.async { [weak self] in
                  guard let self = self else { return }
                  self.didChange.send(self)
              }
          }
      }
      private var task: DownloadTask?
      private let dispatchqueue: DispatchQueue
  
      init(downloader: ImageDownloader = KingfisherManager.shared.downloader,
           cache: ImageCache = KingfisherManager.shared.cache,
           dispatchqueue: DispatchQueue = DispatchQueue.main) {
          self.downloader = downloader
          self.cache = cache
          self.dispatchqueue = dispatchqueue
      }
  
      deinit {
          task?.cancel()
      }
  
      func image(for url: URL?) -&gt; UIImage {
          guard let targetUrl = url else {
              return UIImage.from(color: .gray)
          }
          guard let image = image else {
              load(url: targetUrl)
              return UIImage.from(color: .gray)
          }
          return image
      }
  
      private func load(url: URL) {
          let key = url.absoluteString
          if cache.isCached(forKey: key) {
              cache.retrieveImage(forKey: key) {  [weak self] (result) in
                  guard let self = self else { return }
                  switch result {
                  case .success(let value):
                      self.image = value.image
                  case .failure(let error):
                      print(error.localizedDescription)
                  }
              }
          } else {
              downloader.downloadImage(with: url, options: nil, progressBlock: nil) {  [weak self] (result) in
                  guard let self = self else { return }
                  switch result {
                  case .success(let value):
                      self.cache.storeToDisk(value.originalData, forKey: url.absoluteString)
                      self.image = value.image
                  case .failure(let error):
                      print(error.localizedDescription)
                  }
              }
          }
      }
  
  }
  </code></pre>