Bird
Raised Fist0
Swiftprogramming~10 mins

Global actors (@MainActor) in Swift - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Global actors (@MainActor)
Define @MainActor function
↓
Call function from main thread
↓
Function runs on main thread
↓
Update UI or shared resource safely
↓
Return control to caller
This flow shows how a function marked with @MainActor runs on the main thread to safely update UI or shared resources.
Execution Sample
Swift
import SwiftUI

@MainActor
func updateLabel() {
    print("Updating UI on main thread")
}

Task {
    await updateLabel()
}
This code defines a function marked with @MainActor and calls it asynchronously to ensure it runs on the main thread.
Execution Table
StepActionThreadResultOutput
1Define function updateLabel() with @MainActorMainFunction ready
2Start Task to call updateLabel()BackgroundTask started
3Await updateLabel() callBackgroundSwitch to main thread
4Execute updateLabel() on main threadMainPrint statement runsUpdating UI on main thread
5Return from updateLabel()MainFunction completes
6Task completesBackgroundControl returns
💡 Task finishes after updateLabel() runs on main thread and prints output
Variable Tracker
VariableStartAfter Step 2After Step 4Final
updateLabel()DefinedCalled (await)Executed on main threadCompleted
Key Moments - 2 Insights
Why does updateLabel() run on the main thread even though Task starts on a background thread?
Because updateLabel() is marked with @MainActor, Swift automatically switches execution to the main thread before running it, as shown in step 3 and 4 of the execution_table.
What happens if we call updateLabel() without await inside Task?
You must use await to call an async @MainActor function; otherwise, the compiler will give an error. The await ensures the caller waits for the main thread switch, as seen in step 3.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, on which step does the code switch to the main thread?
AStep 2
BStep 3
CStep 4
DStep 5
💡 Hint
Check the 'Thread' column in execution_table row for step 3 showing 'Switch to main thread'
According to variable_tracker, what is the state of updateLabel() after step 4?
AFunction is defined but not called
BFunction is called but not executed
CFunction executed on main thread
DFunction completed and returned
💡 Hint
Look at variable_tracker row for updateLabel() after step 4
If we remove @MainActor from updateLabel(), what changes in the execution flow?
AFunction runs on background thread without switching
BFunction still runs on main thread automatically
CFunction cannot be called from Task
DFunction causes a compile error
💡 Hint
Without @MainActor, no automatic thread switch happens as shown in step 3 of execution_table
Concept Snapshot
@MainActor marks functions to run on the main thread.
Calls to @MainActor functions switch threads automatically.
Use await to call async @MainActor functions.
Ensures safe UI or shared resource updates.
Helps avoid threading bugs in Swift concurrency.
Full Transcript
This example shows a Swift function marked with @MainActor, which means it must run on the main thread. When we call this function inside a Task, which runs on a background thread, Swift automatically switches to the main thread before running the function. The execution_table traces each step: defining the function, starting the Task, switching threads, running the function, and completing the Task. The variable_tracker shows the function's state changes. Key moments clarify why the thread switch happens and the need for await. The visual quiz tests understanding of thread switching and function execution. Overall, @MainActor helps keep UI updates safe by running code on the main thread.

Practice

(1/5)
1. What is the main purpose of using @MainActor in Swift?
easy
A. To create background tasks without blocking the UI
B. To make code run faster by using multiple threads
C. To encrypt data automatically
D. To ensure code runs on the main thread for UI safety

Solution

  1. Step 1: Understand the role of @MainActor

    @MainActor marks code to run on the main thread, which is important for UI updates.
  2. Step 2: Compare options with this purpose

    Only To ensure code runs on the main thread for UI safety correctly states that @MainActor ensures code runs on the main thread for UI safety.
  3. Final Answer:

    To ensure code runs on the main thread for UI safety -> Option D
  4. Quick Check:

    @MainActor purpose = UI safety on main thread [OK]
Hint: Remember: @MainActor means run on main thread for UI [OK]
Common Mistakes:
  • Thinking @MainActor speeds up code
  • Confusing @MainActor with background threading
  • Assuming @MainActor encrypts data
2. Which of the following is the correct way to declare a function that runs on the main actor?
easy
A. @MainActor func updateUI() { }
B. func @MainActor updateUI() { }
C. func updateUI() @MainActor { }
D. func updateUI() mainActor { }

Solution

  1. Step 1: Recall correct syntax for @MainActor

    The attribute @MainActor is placed before the function declaration.
  2. Step 2: Check each option's syntax

    @MainActor func updateUI() { } correctly places @MainActor before the function name. Others have incorrect placement or syntax.
  3. Final Answer:

    @MainActor func updateUI() { } -> Option A
  4. Quick Check:

    @MainActor syntax = before func [OK]
Hint: Put @MainActor right before func keyword [OK]
Common Mistakes:
  • Placing @MainActor after function name
  • Using invalid keywords like mainActor without @
  • Inserting @MainActor inside function parentheses
3. Consider this Swift code snippet:
@MainActor
class ViewModel {
    func update() {
        print("Updated on main thread")
    }
}

func backgroundTask() async {
    let vm = await ViewModel()
    await vm.update()
}

Task {
    await backgroundTask()
}
What will be printed when this code runs?
medium
A. No output due to error
B. Updated on main thread
C. Updated on background thread
D. Runtime crash due to thread violation

Solution

  1. Step 1: Understand @MainActor on ViewModel class

    The entire ViewModel class runs on the main thread because of @MainActor.
  2. Step 2: Analyze the call to update() with await

    Calling await vm.update() from backgroundTask correctly switches to main thread and runs update(), printing the message.
  3. Final Answer:

    Updated on main thread -> Option B
  4. Quick Check:

    @MainActor enforces main thread execution [OK]
Hint: await switches to main thread for @MainActor code [OK]
Common Mistakes:
  • Expecting no output due to async
  • Thinking update runs on background thread
  • Assuming thread violation crash
4. This code produces a compile error. What is the mistake?
class DataManager {
    @MainActor func save() {
        print("Saving data")
    }
}

func run() {
    let dm = DataManager()
    dm.save()
}
medium
A. Cannot use @MainActor on class methods
B. Function save() must be static
C. Missing await when calling @MainActor async function
D. DataManager must be marked @MainActor

Solution

  1. Step 1: Identify @MainActor function requirements

    Functions marked @MainActor are asynchronous and must be called with await.
  2. Step 2: Check call to save()

    Calling dm.save() without await causes a compile error because save() is async under @MainActor.
  3. Final Answer:

    Missing await when calling @MainActor async function -> Option C
  4. Quick Check:

    @MainActor functions require await calls [OK]
Hint: Always use await when calling @MainActor functions [OK]
Common Mistakes:
  • Calling @MainActor functions without await
  • Thinking @MainActor disallows methods on classes
  • Believing @MainActor requires static functions
5. You want to create a Swift class that updates UI labels safely from any thread. Which is the best way to ensure all UI updates happen on the main thread using @MainActor?
hard
A. Mark the class with @MainActor and call its methods with await from any thread
B. Mark each UI label property with @MainActor individually
C. Use DispatchQueue.global() inside the class methods
D. Mark the class as final and use synchronous methods only

Solution

  1. Step 1: Understand @MainActor usage for UI safety

    Marking the whole class with @MainActor ensures all its code runs on the main thread.
  2. Step 2: Consider calling methods with await

    Calling methods with await from any thread safely switches to main thread for UI updates.
  3. Step 3: Evaluate other options

    Marking individual properties is less effective; DispatchQueue.global() runs on background thread; final keyword does not ensure thread safety.
  4. Final Answer:

    Mark the class with @MainActor and call its methods with await from any thread -> Option A
  5. Quick Check:

    @MainActor class + await calls = safe UI updates [OK]
Hint: Use @MainActor on class and await calls for UI safety [OK]
Common Mistakes:
  • Marking only properties instead of class
  • Using background queues for UI updates
  • Ignoring async/await with @MainActor
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard