3

I want to display an image from a url retrieved in json in my list. How would I Do so?

I tried just calling image and entering the url, but it just shows the space for the image, but not the actual image.

    var body: some View {
        NavigationView {
            List {
                TextField("Search for Meme by name", text: self.$searchItem)


            ForEach(viewModel.memes) { meme in
                HStack {
                    VStack(alignment: .leading, spacing: 2) {
                        Text(meme.name).font(.headline).lineLimit(nil)
                        Image(meme.url).resizable().frame(width: 100, height: 100)

                        }
                    }
                }
            }
        .navigationBarTitle("All Memes")
        }.onAppear {
            self.viewModel.fetchAllMemes()

        }
    }
1
  • 1
    You need to download the image first and this is not part of swiftUI but same as before. Commented Oct 25, 2019 at 21:06

1 Answer 1

1

Make your own view that has its own ObservableObject that downloads (and optionally caches) the image. Here is an example:

import SwiftUI
import Combine
import UIKit

class ImageCache {
  enum Error: Swift.Error {
    case dataConversionFailed
    case sessionError(Swift.Error)
  }
  static let shared = ImageCache()
  private let cache = NSCache<NSURL, UIImage>()
  private init() { }
  static func image(for url: URL) -> AnyPublisher<UIImage?, ImageCache.Error> {
    guard let image = shared.cache.object(forKey: url as NSURL) else {
      return URLSession
        .shared
        .dataTaskPublisher(for: url)
        .tryMap { (tuple) -> UIImage in
          let (data, _) = tuple
          guard let image = UIImage(data: data) else {
            throw Error.dataConversionFailed
          }
          shared.cache.setObject(image, forKey: url as NSURL)
          return image
        }
        .mapError({ error in Error.sessionError(error) })
        .eraseToAnyPublisher()
    }
    return Just(image)
      .mapError({ _ in fatalError() })
      .eraseToAnyPublisher()
  }
}

class ImageModel: ObservableObject {
  @Published var image: UIImage? = nil
  var cacheSubscription: AnyCancellable?
  init(url: URL) {
    cacheSubscription = ImageCache
      .image(for: url)
      .replaceError(with: nil)
      .receive(on: RunLoop.main, options: .none)
      .assign(to: \.image, on: self)
  }
}

struct RemoteImage : View {
  @ObservedObject var imageModel: ImageModel
  init(url: URL) {
    imageModel = ImageModel(url: url)
  }
  var body: some View {
    imageModel
      .image
      .map { Image(uiImage:$0).resizable() }
      ?? Image(systemName: "questionmark").resizable()
  }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.