I'm having an Image holder that would load the thumdnail on init and allow for download later on. My issue is that the view is not updated with the images after I load them. After pressing the load button for the second time, my first images are then displayed.
I'm having trouble finding the reason behind this behaviour.
The image holder :
class MyImage: ObservableObject {
private static let sessionProcessingQueue = DispatchQueue(label: "SessionProcessingQueue")
@Published var thumbnail: UIImage?
@Published var loaded: Bool
var fullName: String {
"\(folderName)/\(fileName)"
}
var onThumbnailSet: ((UIImage?) -> Void)
private var folderName: String
private var fileName: String
private var cancelableThumbnail: AnyCancellable?
private var thumbnailUrl: URL? {
return URL(string: "\(BASE_URL)/thumbnail/\(fullName)")
}
private var downloadUrl: URL? {
return URL(string: "\(BASE_URL)/download/\(fullName)")
}
init(folderName: String, fileName: String) {
self.folderName = folderName
self.fileName = fileName
self.loaded = false
self.loadThumbnail()
}
private func loadThumbnail() {
guard let requestUrl = thumbnailUrl else { fatalError() }
self.cancelableThumbnail = URLSession.shared.dataTaskPublisher(for: requestUrl)
.subscribe(on: Self.sessionProcessingQueue)
.map { UIImage(data: $0.data) }
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { (suscriberCompletion) in
switch suscriberCompletion {
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
}
}, receiveValue: { [weak self] (value) in
self?.objectWillChange.send()
self?.loaded.toggle()
self?.thumbnail = value
})
}
The view :
struct MyView: View {
@ObservedObject var imagesHolder: ImagesHolder = ImagesHolder()
var body: some View {
VStack {
Button(action: {
self.loadImages()
}, label: {
Text("Load images")
})
ForEach(imagesHolder.images, id: \.self) { image in
if image.loaded {
Image(uiImage: image.thumbnail!)
.frame(width: 600, height: 600)
} else {
Text("Not loaded")
}
}
}
}
private func loadImages() -> Void {
loadMediaList(
onLoadDone: { myImages in
myImages.forEach { image in
imagesHolder.append(image)
}
}
)
}
}
The observed object containing the array of loaded images :
class ImagesHolder: ObservableObject {
@Published var images: [MyImage] = []
func append(_ myImage: MyImage) {
objectWillChange.send()
images.append(myImage)
}
}
And finally my data loader :
func loadMediaList(onLoadDone: @escaping (([MyImage]) -> Void)) -> AnyCancellable {
let url = URL(string: "\(BASE_URL)/medias")
guard let requestUrl = url else { fatalError() }
return URLSession.shared.dataTaskPublisher(for: requestUrl)
.subscribe(on: Self.sessionProcessingQueue)
.map { parseJSON(data: $0.data) }
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { (suscriberCompletion) in
switch suscriberCompletion {
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
}
}, receiveValue: { images in
onLoadDone(images);
})
}