top of page

01 · INTRODUCTION

What Is the Main Thread?

The short answer

The main thread is the first and primary stream of instructions created for a running application.

Every line of Swift we write eventually becomes instructions that must be executed by a processor.

CODE EXAMPLE

print("Line 1 — Program started")
print("Line 2 — The next instruction executes")
print("Line 3 — Swift source is compiled into machine code")
print("Line 4 — The processor executes those machine instructions")
print("Line 5 — A thread is one stream through those instructions")

When we read this code, our eyes naturally move from the first line to the second line and then to the third.

That is also the first useful way to imagine its execution.

The statements form one ordered script. After compilation, the resulting machine instructions are executed in sequence along one thread.

To understand the main thread, imagine those instructions packed into shipping containers.

Each container holds one instruction. The containers form a long single-file stream leading towards the processor.

EXECUTION DIAGRAM

OUR SWIFT CODE

let launch = loadLaunch()
launch.status = .ready
display(launch)

        │
        ▼

CONCEPTUAL INSTRUCTION STREAM

┌─────────────┐
│ Instruction │
│      1      │
└─────────────┘
        │
        ▼
┌─────────────┐
│ Instruction │
│      2      │
└─────────────┘
        │
        ▼
┌─────────────┐
│ Instruction │
│      3      │
└─────────────┘
        │
        ▼
   PROCESSOR CORE

The processor takes the instructions from that stream and executes them one after another.

Instruction 1 executes before Instruction 2. Instruction 2 executes before Instruction 3.

Only one instruction from this stream can be executing on one processor core at a particular instant.

This single-file stream gives us our first useful mental image of a thread.

💡 The First Mental Model

A thread is one stream of instructions moving through a running program.

The processor executes the instructions from that stream sequentially: one instruction at a time.

02 · TUTORIAL

From Swift Code to Processor Instructions

The processor cannot read Swift source code directly.

CODE EXAMPLE

let answer = 20 + 22
print(answer)

Before the application runs, the compiler transforms our Swift code into lower-level machine instructions that the processor understands.

EXECUTION DIAGRAM

Swift source code
       │
       ▼
Swift compiler
       │
       ▼
Machine instructions
       │
       ▼
Processor core executes those instructions

One line of Swift does not necessarily become one machine instruction.

A single Swift statement may require several processor instructions. The compiler may also combine, rearrange or remove operations while optimising the program.

Our shipping-container diagram is therefore a teaching model.

At first, we can imagine one container representing one line of Swift because that makes the flow easy to see. The more precise model is that each container represents one machine instruction produced from our compiled program.

EXECUTION DIAGRAM

TEACHING MODEL

One Swift line
      │
      ▼
One instruction container


MORE PRECISE MODEL

One Swift line
      │
      ▼
One or more machine instructions
      │
      ▼
Several instruction containers

The important idea remains the same: the processor ultimately works through an ordered stream of instructions.

03 · TUTORIAL

That Stream of Instructions Is a Thread

A thread is one path of execution through a running program.

It keeps track of the instruction currently being executed, the functions that led to that instruction and where execution should continue when each function returns.

Developers often describe a thread as a stream of instructions because this captures the most important part of the idea: one ordered sequence of work is progressing through the program.

CODE EXAMPLE

func prepareLaunch() {
    loadRocket()
    checkWeather()
    updateStatus()
}

func loadRocket() {
    print("Rocket loaded")
}

If prepareLaunch() is executing on the main thread, that same instruction stream enters loadRocket(), executes its instructions and then returns to the next instruction inside prepareLaunch().

EXECUTION DIAGRAM

MAIN THREAD

Enter prepareLaunch()
        │
        ▼
Call loadRocket()
        │
        ▼
Execute print("Rocket loaded")
        │
        ▼
Return to prepareLaunch()
        │
        ▼
Call checkWeather()
        │
        ▼
Call updateStatus()

It is still one stream.

💡 Important Terminology

Thread = one path of execution through a running process.

Instruction stream = a useful way to imagine the ordered instructions executed along that path.

Call stack = the thread's record of active function calls and where execution must return.

04 · TUTORIAL

Why Call It a "Thread"?

If a computer executed only one continuous path of instructions, we could simply describe that path as the program running.

The word thread becomes useful when the system is concurrent and several paths of execution exist.

Each thread is one continuous strand of execution. The operating system can pause one strand, run another and later return to the first.

The name comes from the real-world image of textile threads. One thread is one strand. Several threads can be woven together.

EXECUTION DIAGRAM

ONE THREAD

One continuous strand of instructions

────────────── A1 ── A2 ── A3 ── A4 ──────────────


A CONCURRENT SYSTEM

Several strands are interleaved over the same period

Thread A      A1 ── A2                A3 ── A4

Thread B                B1 ── B2

System thread                      S1 ── S2

A process with only one path of execution still has one thread. Concurrency makes the distinction important because that thread now exists alongside other threads competing for processor time.

This is why threads belong in an explanation of Swift Concurrency. Before Swift can schedule Tasks within our application, the operating system is already scheduling threads across the complete device.

05 · TUTORIAL

What Is a Process?

When iOS launches an application, it creates a running process for that application.

The process is the application's container while it is running.

It contains the application's memory, resources and threads.

EXECUTION DIAGRAM

RUNNING APPLICATION PROCESS

┌──────────────────────────────────────┐
│                                      │
│  Application memory                  │
│  Application resources               │
│  Open files and network connections  │
│                                      │
│  Main thread                         │
│                                      │
└──────────────────────────────────────┘

The process is not the thread.

The process is the running application container. A thread is one stream of execution inside that container.

An application can contain more than one thread, but every iOS application begins with a primary thread called the main thread.

06 · TUTORIAL

What Happens When the User Taps an App Icon?

Now we can follow an iOS application from the moment it begins.

The user taps the application's icon.

iOS receives a request to launch that application.

The operating system checks that the application can be launched, creates a new process for it when a new process is required, prepares its memory and resources, and creates the process's initial thread.

That initial thread becomes the application's main thread.

EXECUTION DIAGRAM

THE BIRTH OF AN iOS APPLICATION

User taps the app icon
        │
        ▼
iOS receives a launch request
        │
        ▼
iOS prepares the application process
        │
        ├── application memory
        ├── application resources
        └── initial thread
                 │
                 ▼
             MAIN THREAD
                 │
                 ▼
       program entry point begins

A newly launched program has a defined entry point. That entry point tells the system where execution of the program begins.

The application's first instructions begin on its initial thread.

This is why the words main function and main thread appear together so often: the program's main entry point begins executing on the process's initial thread.

💡 The App's First Moment

The process does not begin with every part of the application running.

It begins at one entry point, on one initial thread, with one ordered stream of instructions.

07 · TUTORIAL

The History of the main() Function

Many programming languages use a function named main() as the visible starting point of a program.

Historically, C, C++ and Objective-C programs commonly declared this function directly:

CODE EXAMPLE

func main() {
    print("Program started")
    loadApplicationState()
    createFirstWindow()
}

This Swift function is only a conceptual example. Declaring an ordinary function named main does not by itself make that function the entry point.

The important historical idea is simple:

EXECUTION DIAGRAM

Program is launched
        │
        ▼
main() begins
        │
        ▼
main() starts the program's higher-level systems

Older UIKit applications made this relationship visible through an Objective-C main function:

CODE EXAMPLE

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(
            argc,
            argv,
            nil,
            NSStringFromClass([AppDelegate class])
        );
    }
}

UIApplicationMain performed the framework setup required to create the application object, establish the main event loop and begin processing events.

Modern Swift and SwiftUI hide more of this launch machinery, but the program still requires an entry point.

08 · TUTORIAL

What Does @main Mean in Swift?

Swift's @main attribute marks the type that provides the program's top-level entry point.

CODE EXAMPLE

@main
struct Program {
    static func main() {
        print("Program started")
    }
}

When the program launches, Swift calls the type's static main() method.

The function is static because the program needs somewhere to begin before it has created an instance of Program.

@main is part of the Swift language. It is not a SwiftUI feature.

SwiftUI uses this language feature by placing @main on a type that conforms to the App protocol:

CODE EXAMPLE

import SwiftUI

@main
struct RocketLaunchApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

We do not write the static main() method ourselves in an ordinary SwiftUI application.

The App protocol supplies the launch behaviour. The system calls the app conformer's main() method, and SwiftUI performs the framework setup needed to create the app and its scenes.

EXECUTION DIAGRAM

iOS launches the process
        │
        ▼
Initial thread begins
        │
        ▼
@main identifies RocketLaunchApp
        │
        ▼
RocketLaunchApp.main() launches the SwiftUI app
        │
        ▼
SwiftUI reads the App body's scene declarations
        │
        ▼
WindowGroup provides the application's window scene
        │
        ▼
ContentView becomes part of the initial interface

The body property is not itself an endless loop and it is not a handwritten replacement for every line inside main().

It is a declarative description of the scenes the application can create. SwiftUI and UIKit provide the surrounding application lifecycle and event-processing infrastructure.

09 · TUTORIAL

Why Is It Called the Main Thread?

It is called the main thread because it is the application's first and primary thread of execution.

Important application lifecycle and user-interface responsibilities are organised around it.

It means that this is the central thread around which the application is started and its important user-facing work is coordinated.

EXECUTION DIAGRAM

APP A PROCESS

┌──────────────────────────────────────┐
│                                      │
│  Main thread                         │
│  ┌──────────────────────────────┐    │
│  │ Application instructions     │    │
│  │ Event handlers               │    │
│  │ Important UI work            │    │
│  └──────────────────────────────┘    │
│                                      │
└──────────────────────────────────────┘

This gives us a more complete definition:

💡 What Is the Main Thread?

The main thread is the first and primary path of execution inside a running application process. On iOS, important application and user-interface work is organised around this thread.

10 · TUTORIAL

Five Applications Mean Five Main Instruction Streams

Now imagine that five applications are running.

Each application has its own process.

Each process has its own main thread.

We can therefore imagine five separate main instruction streams flowing towards the processor.

EXECUTION DIAGRAM

FIVE RUNNING APPLICATIONS

App A main thread     ──[A1]──[A2]──[A3]──[A4]──►

App B main thread     ──[B1]──[B2]──[B3]──[B4]──►

App C main thread     ──[C1]──[C2]──[C3]──[C4]──►

App D main thread     ──[D1]──[D2]──[D3]──[D4]──►

App E main thread     ──[E1]──[E2]──[E3]──[E4]──►

                              │
                              ▼
                    OPERATING-SYSTEM SCHEDULER
                              │
                              ▼
                       PROCESSOR CORES

The five instruction streams do not merge themselves into one permanent application queue.

They remain separate threads belonging to separate processes.

The operating-system scheduler decides which runnable thread receives time on an available processor core.

The device also runs operating-system services and background software. Those processes contain threads too.

The processor is therefore not serving only our application. It is continually executing instructions belonging to many threads across the complete system.

11 · TUTORIAL

One Processor Core Accepts One Stream at a Time

Imagine that the device has only one processor core.

That core can execute only one instruction at a particular instant.

The operating system might allow App A's main thread to execute for a short period. It may then pause that thread and allow App B's main thread to execute. It may then execute work belonging to an iOS system thread before eventually returning to App A.

EXECUTION DIAGRAM

ONE PROCESSOR CORE

TIME
 │
 ▼

App A main thread      [A1][A2][A3]

App B main thread                  [B1][B2]

iOS system thread                         [S1][S2][S3]

App C main thread                                      [C1][C2]

App A main thread                                              [A4][A5]

Only one instruction container reaches this core at a time.

While App B executes, App A's main thread remains part of App A's process. When the operating system schedules it again, it continues from its next instruction.

This switching happens quickly enough that many applications and services appear to make progress together.

That is concurrency at the operating-system level.

💡 Important Idea

A thread can exist without currently executing.

Running means the thread is executing instructions on a processor core.

Runnable means the thread is ready to execute but is waiting for processor time.

Waiting means the thread cannot continue until an event or result becomes available.

12 · TUTORIAL

Several Processor Cores Can Accept Several Streams

Modern devices contain several processor cores.

This means the operating system may execute instructions from more than one thread at the same time.

EXECUTION DIAGRAM

TWO PROCESSOR CORES

TIME
 │
 ▼

Core 1     App A [A1][A2]     App C [C1][C2]

Core 2     App B [B1][B2]     System [S1][S2]

           ◄── parallel ──►    ◄── parallel ──►

When two cores execute two instruction streams at the same instant, the work is executing in parallel.

When several streams make progress over the same period, even if one core switches between them, the system is concurrent.

The main thread is one runnable thread among many threads competing for the device's available execution resources.

13 · TUTORIAL

An Application Can Have More Than One Thread

The main thread is the application's first instruction stream, but it does not have to be the only one.

A process can contain additional threads.

EXECUTION DIAGRAM

ONE APPLICATION PROCESS

┌────────────────────────────────────────────┐
│                                            │
│  Main thread       ──[M1]──[M2]──[M3]──►  │
│                                            │
│  Worker thread 1   ──[W1]──[W2]──[W3]──►  │
│                                            │
│  Worker thread 2   ──[X1]──[X2]──[X3]──►  │
│                                            │
└────────────────────────────────────────────┘

Each thread is a separate path of execution inside the same process.

The operating system can schedule these threads independently.

On a single core, it can switch between them. On several cores, some of them may execute in parallel.

Additional threads allow the process to contain additional streams of execution, but they also introduce the possibility that multiple streams may access the same memory.

This is one reason concurrency requires careful coordination.

Modern Swift applications normally express asynchronous work using tasks and actors rather than manually creating a new thread for every operation. The system still uses threads underneath.

14 · TUTORIAL

A Thread Is Not Literally a Queue

Our single-file stream looks like a queue, and it is useful to imagine instructions waiting in line.

But a thread and a queue are not technically the same object.

A queue stores and orders submitted work.

A thread is the execution context that runs instructions.

EXECUTION DIAGRAM

QUEUE

Stores waiting work
        │
        ▼
    [Work 1]
    [Work 2]
    [Work 3]


THREAD

Provides a path on which instructions execute
        │
        ▼
Current instruction ──► Processor core

Grand Central Dispatch, for example, allows us to submit closures to dispatch queues. The system then arranges for that work to execute using threads.

The main dispatch queue is a serial queue associated with execution on the main thread.

This distinction lets us keep the useful instruction-line metaphor without confusing a thread with Swift's queue abstractions.

💡 Precise Language

Think of a thread as a single-file instruction stream.

Do not conclude that a thread and a dispatch queue are the same thing.

15 · TUTORIAL

Why Does the Main Thread Matter to the Interface?

UIKit and SwiftUI organise important user-interface work around the application's main execution domain.

The main thread is used to process events and perform work needed to keep the visible application up to date.

This means our own main-thread instructions do not own that stream exclusively.

The frameworks also need opportunities to use it.

EXECUTION DIAGRAM

ONE MAIN-THREAD EXECUTION OPPORTUNITY

TIME
 │
 ▼

┌──────────────────────────────────────┐
│ Our synchronous application code    │
│ ███████████████                      │
│                                      │
│ Important UI-related work            │
│                       ███████        │
└──────────────────────────────────────┘

This is a conceptual model.

It does not mean that SwiftUI performs exactly one render at the end of every loop or that every internal rendering operation runs on the main thread.

It means that important parts of event handling and UI coordination depend on the main execution domain becoming available.

If our synchronous code returns quickly, the system can continue processing other main-thread-dependent work.

16 · TUTORIAL

What Happens After the App Has Started?

The application's first launch instructions do not run forever.

They establish the application object, connect the framework lifecycle, create the first scene and prepare the initial interface.

After that startup work has completed, the main thread does not terminate.

The application has entered an event-driven lifetime.

Its main run loop can wait quietly until there is work for the main thread to process.

EXECUTION DIAGRAM

THE LIFE OF THE MAIN THREAD

TIME
 │
 ▼

App entry point begins
 │
 ▼
Framework and application startup
 │
 ▼
Create the first scene and interface
 │
 ▼
Wait for an event
 │
 ├── user touches the screen
 ├── timer becomes ready
 ├── system delivers a lifecycle event
 ├── scheduled main-queue work becomes ready
 └── asynchronous result needs main-thread handling
 │
 ▼
Run the corresponding handler
 │
 ▼
Allow pending UI update and commit work to progress
 │
 ▼
Return to waiting for the next event

The word wait matters.

The run loop does not need to spin continuously and ask sixty times every second whether the user has touched the screen.

It can sleep efficiently until an input source, timer or scheduled item makes work available.

When work arrives, the main thread wakes, runs the relevant instructions and eventually returns to waiting.

This is why an application can remain alive even when no line of our own code is currently executing.

💡 The App Does Not End After Startup

main() starts the application's framework-controlled lifetime.

The main run loop then helps keep the initial thread available for events, callbacks and important interface work until the application is terminated.

17 · TUTORIAL

Does the Main Run Loop Execute 60 Times Per Second?

No.

A 60 Hz display creates presentation opportunities approximately sixty times per second. That display rhythm is not the same thing as the main run loop executing exactly sixty complete loops per second.

A run-loop cycle can process different amounts of work. It can wait when nothing is ready. Several events may be handled before a display update, and the frameworks may coalesce several state changes into one visible result.

EXECUTION DIAGRAM

DISPLAY TIMING

Presentation opportunities may arrive at 60 Hz or 120 Hz


MAIN RUN LOOP

Wait ──► handle ready work ──► wait ──► handle more work


These systems cooperate, but they are not the same clock.

The useful connection is this:

When the display system and UI frameworks need main-thread-dependent work to progress, the main thread must be available.

If our synchronous code is still occupying that instruction stream, the required UI work starts later.

18 · TUTORIAL

Why Is UI Work Organised Around the Main Thread?

The application begins on its initial thread, but history alone is not the complete reason UI work remains there.

UIKit and SwiftUI deliberately organise important interface state and event handling around the main execution domain.

A user interface contains a large connected graph of state: windows, scenes, views, gestures, layout, animations and presentation transactions.

Keeping important access to that graph on one serial execution domain gives the frameworks an ordered model:

EXECUTION DIAGRAM

Touch arrives
     │
     ▼
Main-thread handler changes application state
     │
     ▼
Framework observes that UI work is required
     │
     ▼
Main-thread-dependent update work progresses
     │
     ▼
Rendering is committed for later presentation

This does not mean that every rendering operation happens on the main thread. Other framework components, rendering services and the GPU also participate.

It means that the ordered application-facing side of the interface is intentionally coordinated through the main execution domain.

So the complete answer is:

💡 Why Does UI Work Use the Main Thread?

The application starts on its initial thread, and Apple's application frameworks deliberately build their event and UI lifecycle around that same primary serial execution domain.

This keeps important user-interface operations ordered and gives the application one predictable place from which to coordinate visible state.

19 · TUTORIAL

What Does Blocking the Main Thread Mean?

Blocking the main thread means keeping its instruction stream occupied so that other main-thread-dependent work cannot execute when it needs to.

Consider this deliberately bad example:

CODE EXAMPLE

@MainActor
func calculateLaunchWindow() {
    var result = 0

    for value in 1...500_000_000 {
        result &+= value
    }

    print(result)
}

The main thread begins executing the loop.

Each iteration produces more instructions for the processor to execute.

The function does not return until the complete loop has finished.

EXECUTION DIAGRAM

MAIN THREAD

TIME
 │
 ▼

Our loop
██████████████████████████████████████████████████

Pending UI-related work
                                                  ███████
                                                  ▲
                                                  │
                                  begins much later than required

The operating system may interrupt this thread to execute threads belonging to other processes.

That does not help the UI work waiting for this application's same main execution domain. When the application receives more processor time, its main thread still has to continue the synchronous loop before it can return to other work.

The application may not have crashed.

The main instruction stream is simply occupied by our function.

The user experiences the result as an unresponsive interface: touches appear late, scrolling stops, animations stall and visible state changes are delayed.

💡 The Meaning of “Do Not Block the Main Thread”

Do not place a long uninterrupted sequence of synchronous instructions into the same execution stream required by important user-interface work.

20 · TUTORIAL

How Swift Concurrency Changes the Model

Swift Concurrency adds a higher-level way to organise work into tasks that can suspend and resume while the system continues to use threads underneath.

CODE EXAMPLE

@MainActor
func waitForLaunch() async throws {
    status = "Waiting"

    try await Task.sleep(for: .seconds(2))

    status = "Ready"
}

When the task reaches await and genuinely needs to wait, it can suspend.

Suspending the task allows the main execution domain to run other eligible work instead of blocking its thread for the complete wait.

EXECUTION DIAGRAM

MAIN-ACTOR TASK

TIME
 │
 ▼

Execute: status = "Waiting"
 │
 ▼
Reach a suspension point
 │
 ├──────── Task is suspended ────────┐
 │                                    │
 │  Main thread can execute other    │
 │  eligible work                    │
 │                                    │
 └──────── Task becomes ready ───────┘
 │
 ▼
Execute: status = "Ready"

await marks a place where the asynchronous operation may need to wait and the current task may stop executing temporarily.

That is how Swift Concurrency connects to the main thread: it allows waiting tasks to step out of the execution stream without requiring us to block the thread while nothing useful can happen.


21 · TUTORIAL

The Complete Mental Model

EXECUTION DIAGRAM

OUR SWIFT SOURCE CODE
        │
        ▼
COMPILER CREATES MACHINE INSTRUCTIONS
        │
        ▼
RUNNING APPLICATION PROCESS
        │
        ├── Main thread
        │     One primary instruction stream
        │
        ├── Other system-managed threads
        │     Additional instruction streams
        │
        ▼
OPERATING-SYSTEM SCHEDULER
        │
        │ chooses runnable threads
        ▼
PROCESSOR CORES
        │
        │ execute machine instructions
        ▼
THE APPLICATION CHANGES STATE AND PRODUCES RESULTS

Every running application has its own process.

Every application process begins with a main thread.

Each thread represents one path through the application's compiled instructions.

The operating system schedules runnable threads onto a limited number of processor cores.

The processor executes the selected stream's machine instructions one after another.

The application's main thread is special because important application lifecycle and interface work is organised around it.

If our synchronous code fills that instruction stream for too long, other work requiring the same main execution domain must wait.

That is the main thread.


22 · TUTORIAL

What to Remember

💡 What to Remember

1. Swift source code is compiled into machine instructions before the processor executes it.

2. A thread is one path of execution through a running process.

3. We can imagine a thread as a single-file stream of instruction containers flowing towards a processor core.

4. One container representing one Swift line is a teaching simplification. A Swift line may produce several machine instructions.

5. Every running iOS application has its own process and begins with its own main thread.

6. The operating system schedules runnable threads from applications and system services onto the available processor cores.

7. On one core, only one instruction stream executes at a particular instant.

8. A thread is not technically the same thing as a dispatch queue.

9. Important UI work depends on the application's main execution domain becoming available.

10. Blocking the main thread means occupying that instruction stream for too long.

11. Swift Concurrency lets Tasks suspend instead of unnecessarily blocking a thread while they wait.


23 · TUTORIAL

Frequently Asked Questions

What is the main thread in an iOS application?

The main thread is the first and primary path of execution inside the application's running process. Important application lifecycle and user-interface work is organised around this execution domain.

Does every iOS application have its own main thread?

Yes. Every running application has its own process, and that process begins with its own main thread. The operating system schedules that thread alongside threads belonging to other applications and system services.

Is the main thread a serial queue?

Not technically. A thread is an execution context, while a queue stores and orders submitted work. It is useful to imagine the main thread as one serial stream of instructions, but the main dispatch queue and the main thread are different abstractions.

Does the processor execute one line of Swift at a time?

The processor executes machine instructions rather than Swift source lines. One Swift line may become several machine instructions. Imagining one source line as one instruction container is a useful first model that must later be refined.

Why does blocking the main thread freeze the interface?

Important interface work depends on the main execution domain. If a long synchronous function occupies that stream, other main-thread-dependent work cannot execute until the function returns.


24 · TUTORIAL

Continue Learning Swift Concurrency

Read What Is Swift Concurrency? to continue from operating-system thread scheduling into Swift's task-based scheduling model.

Apple's Understanding Hangs in Your App documentation explains how unavailable main-thread time becomes a visible application hang.


25 · TUTORIAL

Download Xcode Playground

The accompanying How an iOS App Begins Xcode playground will turn this mental model into an executable study guide.

It will explore top-level playground execution, the history of main(), Swift's @main attribute, the SwiftUI App entry point, the initial main thread, the event-driven application lifetime, blocking and the connection to Swift tasks.

Download the playground from this article, open it in Xcode, keep the console visible and rerun each example while changing the delays and workloads.

The article builds the mental picture. The playground lets you watch the instruction streams execute.

bottom of page