2

I'm trying to write a code that loads more than one image (between 5 and 10) from url. I use Theaudiodb api (loaded images are photos of artists). The problem is that only last image is displayed. In ContentView I have ForEach that loads between 5 and 10 ArtistImage(s) struct. I know that all images data are loaded (in ImageFetcher var imageData will set I put print(newValue) and it shows data loaded (for example: 148273 bytes ,etc). Just cannot understand why just the last image is displayed.

struct ArtistImage: View {

var imageURL: URL?

@ObservedObject private var imageFetcher: ImageFetcher

init(imageURL: String) {
    self.imageURL = URL(string: imageURL)

    imageFetcher = ImageFetcher(imageURL: self.imageURL)
}

var body: some View {

    if let uiImage = UIImage(data: imageFetcher.imageData) {
        return AnyView(Image(uiImage: UIImage(data: self.imageFetcher.imageData)!)
            .renderingMode(.original)
            .resizable()
            .cornerRadius(10))
    } else {
        return AnyView(Text("Loading...")
            .onAppear(perform: { self.imageFetcher.getImage() }))
    }
}

and the ImageFetcher class below:

class ImageFetcher: ObservableObject  {

public let objectWillChange = PassthroughSubject<Data,Never>()

public private(set) var imageData = Data() {
    willSet {
        print(newValue)        // it tells me all images are loaded
        objectWillChange.send(newValue)
    }
}
var imageURL: URL?

public init(imageURL: URL?) {
    self.imageURL = imageURL
}

func getImage() {

    guard let url = imageURL else { return }

    URLSession.shared.dataTask(with: url) { (data, _, _) in
        guard let data = data else {
            return
        }
        DispatchQueue.main.async {
            self.imageData = data

        }
    }.resume()
}

Content View:

struct ContentView: View {

@ObservedObject var networkManager = NetworkManager()

init() {
    networkManager.createArtistsDatabase()
}
var body: some View {

    ScrollView(.vertical) {

        GeometryReader { geo in

            Button( action: {} ) {
                ZStack(alignment: .bottom) {
                    Image("cp")
                        .renderingMode(.original)
                        .aspectRatio(contentMode: .fill)
                        .frame(width: geo.size.width, height: geo.size.height)
                        .clipped()
                    VStack(alignment: .center) {
                        Text("Featured artist").foregroundColor(.yellow).font(.custom("AvenirNext-Regular",size: 16)).padding(.bottom, 8)
                        Text(("Coldplay").uppercased()).foregroundColor(.white).font(.custom("AvenirNext-Bold", size:24))

                    }.frame(width: geo.size.width, height: 80).background(Color.black.opacity(0.5))
                }
            }
        }.frame(height: 400.0)

        VStack(alignment:.leading){
            Text("Discover new artists").font(.custom("AvenirNext-Regular", size: 16)).padding(.top,10)
            ScrollView(.horizontal) {
                HStack {
                    ForEach(self.networkManager.artistsDB, id:\.self) { data in

                        Button(action:{} ) {
                            VStack {
                                ArtistImage(imageURL: data.artistImage)
                                    .frame(width: 150, height: 150)
                                    .cornerRadius(10)
                                Text(data.artistName).foregroundColor((.secondary)).font(.custom("AvenirNext-Regular", size: 14))
                            }
                        }
                    }
                }.frame(height: 180)
            }
        }.frame(minWidth: 0, maxWidth:.infinity,maxHeight: .infinity, alignment: .leading).padding(.leading, 10)
    }.edgesIgnoringSafeArea(.top)
}

}

Network Manager class:

class NetworkManager: ObservableObject {

@Published var artistsDB = [Artist]()

func createArtistsDatabase() {

    let artistsNames: [String] = ["Madonna", "Metallica","Coldplay","Toto","Kraftwerk","Depeche%20Mode"]
    for ar in artistsNames {
        findArtistBy(name: ar)
    }
}

func findArtistBy(id: String = "", name: String = "") {
    var url: URL?

    if id != "" {
        guard let urlID = URL.getArtistByID(id: id) else { return }
        url = urlID

    } else {
        guard let urlName = URL.getArtistByName(name: name) else { return }
        url = urlName

    }
    let urlRequest = URLRequest(url: url!)
    let task = URLSession.shared.dataTask(with: urlRequest) { data,response,error in

        if error != nil || data == nil {
            print("Client error")
            return
        }
        guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
            print("Server error")
            return
        }
        guard let mime = response.mimeType, mime == "application/json" else {
            print("Wrong mime type")
            return
        }
        guard let dataToDecode = data else { return }

        do {
            let decoder = JSONDecoder()
            let dataJSONed = try decoder.decode(DBArtist.self, from: dataToDecode)
            DispatchQueue.main.async {
                print(dataJSONed.artist[0])
                self.artistsDB.append(dataJSONed.artist[0])
            }
        } catch {
            print("Error while decoding!")
        }

    }
    task.resume()
}

URL extension:

extension URL {
static func getArtistByID(id: String) -> URL? {
    return URL(string: "https://theaudiodb.com/api/v1/json/1/artist.php?i=\((id))")
}
static func getArtistByName(name: String) ->URL? {
    print("https://www.theaudiodb.com/api/v1/json/1/search.php?s=\(name)")
    return URL(string: "https://www.theaudiodb.com/api/v1/json/1/search.php?s=\(name)")
}

}

enter image description here

Piotr979
  • 199
  • 1
  • 2
  • 10

0 Answers0