How to prevent actor reentrancy resulting in duplicative requests?

UPDATE

Apple’s developer web site now includes the code snippets for WWDC videos (at least for 2021 and later). You can find the “better solution” code on the video’s page by tapping the “Code” tab under the video player and scrolling down to “11:59 – Check your assumptions after an await: A better solution”.

ORIGINAL

You can find the “better solution” code in the Developer app. Open the session in the Developer app, select the Code tab, and scroll to “11:59 – Check your assumptions after an await: A better solution”.

screen shot of Developer app

The screen shot is from my iPad, but the Developer app is also available on iPhone, Mac, and Apple TV. (I don’t know if the Apple TV version gives you a way to view and copy the code, though…)

As far as I can tell, the code is not available on the developer.apple.com web site, either on the WWDC session’s page or as part of a sample project.

For posterity, here is Apple’s code. It is extremely similar to that of Andy Ibanez:

actor ImageDownloader {

    private enum CacheEntry {
        case inProgress(Task<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 task):
                return try await task.value
            }
        }

        let task = Task {
            try await downloadImage(from: url)
        }

        cache[url] = .inProgress(task)

        do {
            let image = try await task.value
            cache[url] = .ready(image)
            return image
        } catch {
            cache[url] = nil
            throw error
        }
    }
}

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)