What is @MainActor In Swift?

Today I want to explain a simple topic: the use of @MainActor when you're using Swift.

I came across this macro while programming async with SwiftUI, so I want to share my findings with you.

Let's start with a scenario so we can understand the problem that this macro solves.

Scenario

You're developing a fantastic app using SwiftUI.

This app fetches a list of cat pictures from the web.

When the user opens the app, it will request a list of cat pictures from an API.

func fetchData() async throws -> Data {
    let url = URL(string: "https://thecatapi.com")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

When you're using, try await you're doing the call asynchronously.

Async allows to stay responsive by not blocking the thread while waiting for operations (like network requests).

Multiple pieces of code can be run simultaneously if you run the code in parallel. This means you run your code faster (if you can split it, you will take less time to run it).

When writing the code to work asynchronously, you're using concurrency.

"A program that uses parallel and asynchronous code carries out multiple operations at a time (...)  Parallel code means multiple pieces of code run simultaneously [1]"

When you use async code you can perform an action like an API request through the background threads.

You can have many threads running at the same time, but you will always have the main thread where you do operations such as updating the UI.

Apple suggests that you avoid using the main thread for api requests, for example:

If your app has a long running task, such as making network call, run it on a global system queue, or on another background dispatch queue. Alternatively, use asynchronous versions of the call, if available [2].

Now, if you make the requests on a background thread, like an API call, how can you update the UI to show what you received?

That's where @MainActor comes in.

By adding it, you're saying that you want this code to run on the main thread, it's as simple as that.

A singleton actor whose executor is equivalent to the main dispatch queue [3].

Now the main thread updates the UI accordingly.

What If You Don't Use The Main Thread?

I know what you're thinking, what if I don't want to use this approach?

What if I want the background thread to do UI updates, what happens then?

To put it simply, Apple enforces the developers to use the main thread to do UI updates:

"Manipulations to your app’s user interface must occur on the main thread. Thus, you should always call the methods of the UIView class from code running in the main thread of your app [3]."

But if you want to try it anyway, most likely you will get a crash, so be careful. 💥

If you enjoyed this article, make sure you take a look at the full proposal of the macro in the 0316: Global Actors official document.

🚀
Thank you for reading. If you enjoyed this article, consider supporting me by following me on LinkedInGitHub, and X. Let's connect and grow together! ⭐️

References

[1] Swift's Official Documentation: Concurrency

[2] Apple's Official Documentation: Main Queue

[3] Apple's Official Documentation: @Main Actor

Subscribe to Coding With Vera

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe