In WWDC21's Swift Concurrency: Behind the Scenes video, toward the end of the video they share this code example:
// on database actor
func loadArticle(with id: ID) async throws -> Article
@MainActor func updateUI(for article: Article) async
@MainActor func updateArticles(for ids: [ID]) async throws {
for id in ids {
let article = try await database.loadArticle(with: id)
await updateUI(for: article)
}
}
I wanted to first re-create the code snippet and then understand it. (I couldn't get it exactly right):
actor Database {
func loadArticle(with id: Article.ID) async throws -> Article {
// ...
}
}
actor HealthFeed {
let database = Database()
@MainActor func updateUI(for article: Article) async { /* ... */ }
@MainActor func updateArticles(for ids: [Article.ID]) async throws {
for id in ids {
let article = try await database.loadArticle(with: id)
await updateUI(for: article)
}
}
}
struct Article: Identifiable {
let id: Int
}
I need help in understanding the "behind the scenes" work going on, more specifically, why that @MainActor
on updateArticles(for:)
is required.
Since updateUI(for:)
is declared with @MainActor
, it is executed on the main thread. Which makes sense since it's updating the UI. However, updateArticles(for:)
calls updateUI(for:)
, so why does updateArticles(for:)
itself need to be declared with @MainActor
? Seems like an unnecessary context switch to me.
If someone can make me understand why I'm wrong in thinking it's an unnecessary context switch please tell me. Additionally, if someone can better recreate the code snippet from the WWDC video please let me know.