title: SwiftUI @ObservedObject does not get updated on Async fetch of the image
body: <p>I have an app that fetches a list of items with images URL's from remote API, and then it has to fetch an image per item from given url inside that item's position. 
  The problem is that when scrolling up and down and thus removing list items from view and moving them back into view they do show up. However on initial load they stay in "loading" status forever until moved out and in. 
  My code: </p>
  
  <pre><code>import SwiftUI
  
  struct ContentView: View {
  
      @EnvironmentObject var artObjectStore: ArtObjectStore
      @State private var pageCount = 1
      @State private var tappedLink: String? = nil
      @Environment(\.imageCache) var cache: ImageCache
  
      var body: some View {
          NavigationView {
              Form {
  
                  Section(header: Text("Art")) {
  
                      List {
                          ForEach(artObjectStore.artObjects, id: \.self) { artObject in
                              self.link(for: artObject)
  
                          }
                          Button(action: loadMore) {
                              Text("")
                          }
                          .onAppear {
                              DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 10)) {
                                  self.loadMore()
                              }
                          }
                      }
                  }
              }
              .navigationBarTitle("Art objects")
          }
          .onAppear(perform: loadMore)
  
      }
  
      func loadMore() {
          pageCount += 1
          artObjectStore.loadMore(pageCount)
      }
  
      private func link(for artObject: ArtObject) -&gt; some View {
          let selection = Binding(get: { self.tappedLink },
              set: {
                  UIApplication.shared.endEditing()
                  self.tappedLink = $0
          })
  
          return NavigationLink(destination: DetailView(artObject: artObject, cache: self.cache),
                                tag: artObject.id,
                                selection: selection) {
              HStack(alignment: .center) {
                  VStack(alignment: .leading){
                      Text("\(artObject.title)").font(.system(size: 12))
                      Text("\(artObject.principalOrFirstMaker)").font(.system(size: 9)).foregroundColor(.gray)
                  }
                  Spacer()
                  AsyncImage(
                      url: URL(string: artObject.headerImage.url)!,
                      cache: self.cache,
                      width: 200,
                      height: 50
                  )
              }
          }
      }
  }
  
  
  extension UIApplication {
      func endEditing() {
          sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
      }
  }
  
  struct ContentView_Previews: PreviewProvider {
      static var previews: some View {
          ContentView()
      }
  }
  </code></pre>
  
  <p><strong>Image container:</strong> </p>
  
  <pre><code>import SwiftUI
  import Combine
  import Foundation
  
  class ImageLoader: ObservableObject {
  
      let objectWillChange = ObservableObjectPublisher()
  
      private var cancellable: AnyCancellable?
      @Published var image: UIImage? {
          willSet {
              objectWillChange.send()
          }
      }
  
  
      private let url: URL
  
      private var cache: ImageCache?
  
      init(url: URL, cache: ImageCache? = nil) {
          self.url = url
          self.cache = cache
      }
  
      deinit {
          cancellable?.cancel()
      }
  
      private func cache(_ image: UIImage?) {
          image.map { cache?[url] = $0 }
      }
  
      func load() {
          if let image = cache?[url] {
              self.image = image
              return
          }
  
          cancellable = URLSession.shared.dataTaskPublisher(for: url)
              .map { UIImage(data: $0.data) }
              .replaceError(with: nil)
              .handleEvents(receiveOutput: { [weak self] in self?.cache($0) })
              .receive(on: DispatchQueue.main)
              .assign(to: \.image, on: self)
  
  
      }
  
      func cancel() {
          cancellable?.cancel()
      }
  }
  
  
  struct AsyncImage: View {
      @ObservedObject private var loader: ImageLoader
      private let width: CGFloat?
      private let height: CGFloat?
      @State var spin = false
  
      init(url: URL, cache: ImageCache? = nil, width: CGFloat? = nil, height: CGFloat? = nil) {
          loader = ImageLoader(url: url, cache: cache)
          self.width = width
          self.height = height
      }
  
      var body: some View {
          image
              .onAppear(perform: loader.load)
              .onDisappear(perform: loader.cancel)
      }
  
      private var image: some View {
          Group {
              if loader.image != nil {
                  Image(uiImage: loader.image!)
                      .resizable()
                      .aspectRatio(contentMode: .fit)
                      .frame(width: width, height: height)
              } else {
                  Image("loadingCircle")
                      .resizable()
                      .frame(width: 20, height: 20)
                      .rotationEffect(.degrees(spin ? 360 : 0))
                      .animation(Animation.linear(duration: 0.8).repeatForever(autoreverses: false))
                      .onAppear() {
                          self.spin.toggle()
                  }
              }
          }
      }
  }
  
  protocol ImageCache {
      subscript(_ url: URL) -&gt; UIImage? { get set }
  }
  
  struct TemporaryImageCache: ImageCache {
      private let cache = NSCache&lt;NSURL, UIImage&gt;()
  
      subscript(_ key: URL) -&gt; UIImage? {
          get { cache.object(forKey: key as NSURL) }
          set { newValue == nil ? cache.removeObject(forKey: key as NSURL) : cache.setObject(newValue!, forKey: key as NSURL) }
      }
  }
  
  
  struct ImageCacheKey: EnvironmentKey {
      static let defaultValue: ImageCache = TemporaryImageCache()
  }
  
  extension EnvironmentValues {
      var imageCache: ImageCache {
          get { self[ImageCacheKey.self] }
          set { self[ImageCacheKey.self] = newValue }
      }
  }
  </code></pre>
  
  <p>I did try to add willSet on the image, that doesn't seem to work. Can you help me?</p>