When showing an ImageDownloader actor, the presenter said a solution on avoiding duplicate downloads is shown with the code associated with this video. When will this code be available? I don't see it on the video page.
Associated Code?
Came here with the same question 😂
actor ImageDownloader {
private enum CacheEntry {
case inProgress(Task.Handle<Image, Error>)
case ready(Image)
}
private var cache: [URL: CacheEntry] = [:]
func image(from url: URL) async throws -> Image? {
if let cached = cache[url] {
switch cached {
case .ready(let image):
return image
case .inProgress(let handle):
return try await handle.get()
}
}
let handle = async {
try await downloadImage(from: url)
}
cache[url] = .inProgress(handle)
do {
let image = try await handle.get()
cache[url] = .ready(image)
return image
} catch {
cache[url] = nil
throw error
}
}
}
please show downloadImage func example ?
The second task avoiding entire download action depends on the downloadImage() ?
I still don't see the code available, glad to see I'm not crazy... (well at least not this time)
Just slightly tweaking @mayoff's answer which seems sensible to me (accounting for the deprecations):
struct Image {
let url: URL
}
actor ImageDownloader {
private var cache = [URL: Task<Image, Error>]()
func image(for url: URL) async throws -> Image {
if let imageTask = cache[url] {
switch await imageTask.result {
case .success(let image):
return image
case .failure:
cache[url] = nil
}
}
let imageTask = Task {
try await downloadImage(url: url)
}
cache[url] = imageTask
switch await imageTask.result {
case .success(let image):
return image
case .failure(let error):
cache[url] = nil
throw error
}
}
private func downloadImage(url: URL) async throws -> Image {
print("Downloading image - \(url.absoluteString)")
try await Task.sleep(nanoseconds: 2 * 1_000_000_000)
return Image(url: url)
}
}
And you can test this using:
let imageDownloader = ImageDownloader()
async let image1 = try imageDownloader.image(for: URL(string: "https://test.com")!)
async let image2 = try imageDownloader.image(for: URL(string: "https://test.com")!)
async let image3 = try imageDownloader.image(for: URL(string: "https://test.com")!)
async let anotherImage = try imageDownloader.image(for: URL(string: "https://another.test.com")!)
print(try await image1.url.absoluteString)
print(try await image2.url.absoluteString)
print(try await image3.url.absoluteString)
print(try await anotherImage.url.absoluteString)
Which should give:
Downloading image - https://test.com
Downloading image - https://another.test.com
https://test.com
https://test.com
https://test.com
https://another.test.com
Which shows only two calls to downloadImage(url:)
were actually made.