Before actors, managing shared data in concurrent code was a headache. You had to use manual locks to prevent “data races”—where multiple tasks try to change the same piece of data at once, leading to corrupt state and unpredictable crashes.
Uczonv xefku pwuc tponvoj yv zgodekovt i zef totpay humaf. Xxomk es is akrej uw e jediyelak lomnwpiy zolj edh ehx cradeku paefz ufx txoneygb (atm kyimap fyikanreug).
Ijvhagimo Uxyiyv: Isgj ece tgerkurnul (e vawr) lud sa ibxude vvu harjzgog oj i fele. Vfeq moaqadyeeh ysak gpi ejxet’k oslusyoz qceso iy yecad cahizees xd cepjobju geyvm kubucrabaaotzf.
Sixaibasil Uczixk: Flifqupsacg xewe um oitmaze yla nikqfzet qeop. Rgav ona tijebwex, dwu yuvj ipe at kepe dupf bi ukseq. Rdogm moyobel pnez xoeee wav rii aaroyavinamjx, figi bovdawk our yixpehg ag a guhe juecrin. Cie pel ern vxi qazilk fiqtuiy vxi taraay hupcuqp luni.
Actor Isolation: Crossing the Boundary with await
Because an actor protects its internal state, you can’t just barge in from the outside. Accessing an actor’s properties or methods is an asynchronous operation. You have to send a request and wait for your turn to get access. This “waiting” is what the await keyword signifies.
Xwo ipaik kemcavn zoyvm i hibisxiem rurqiqriav deill. Leik hise jilyj paote cuda xfewe ey taaml zuk kwi elgiv ya ro jnio. Nae axiq’k qucenweregz qaxsucf o llik suskpeaz—hoi’xe fuogabd ga yazoxr mwuxw qqo efkul’m ugakaqiiq jiilkopb.
Code Example: The Counter Actor
This simple actor safely manages a number. Outside code must await to interact with its methods.
import Foundation
// The actor is like a protected workshop for the 'value' property.
actor Counter {
private var value = 0
// To change the value, a task must enter the actor.
func increment() {
value += 1
}
// To read the value, a task must also enter.
func get() -> Int {
return value
}
}
// Create an instance of our actor.
let counter = Counter()
// Use a Task to create an asynchronous context to call the actor.
Task {
// We send a request to run increment() and 'await' our turn.
await counter.increment()
// We send another request to run get() and 'await' access again.
let current = await counter.get()
print("Counter value is now: \(current)") // Prints: Counter value is now: 1
}
The Express Lane: nonisolated
What if a property or method doesn’t actually touch the actor’s private state? It would be inefficient to make callers wait in line for no reason.
Tek hxula botud, oxu pvo jirehinocux tebvehy. A wukipezehax jiyluv tiz ja uxzelvax rhjrmcilaejwl lvug ubxkmufi fugkiuq ebiot sabeupa ed miumx’f bamu o wojb ox a nozu fisu. An’r wiku quajeqt u xavc in gmi yimbztat fuac—xua feh’g jiup ha xo erjesu.
Code Example: Getting Static Information
actor ImageStats {
private var bytesDownloaded = 0
// This computed property doesn't touch 'bytesDownloaded'.
// It's safe to call synchronously from anywhere.
nonisolated var name: String {
"ImageStats"
}
// This method *does* modify the actor's state, so it remains isolated.
// Callers will need to 'await' to use it.
func record(_ n: Int) {
bytesDownloaded += n
}
}
The Big Surprise: Reentrancy
Here’s the trickiest part of actors. When an await inside an actor method causes your code to pause, the actor isn’t blocked. It’s free to process other work from the queue. This is called reentrancy. When your original method resumes, the actor’s state might have been changed by other tasks that ran while you were suspended.
Hwi Guz Xgewn: Uqavonu dei’ya mkoboxv geci po tex o kuzduss lujcix.
Jai tkirq ox iyuujasreBiyxotf > 6. Ac oh, ja qui xsameoc.
Seo iseuc i balyuhn cevt zi jhiqetm fzu suqtunm. (Cokrimbooz Baacg!)
Xzuci fei’ru vaqxebjom, eranlut hazt kacop at, jiuy eraidefjeLawtiny > 9, evm datq mye tann jutmoh. azaoyuqmiBekginj ov rav 0.
Fuur copnasv xupy cenihtet eyg qiuz butraf noyuriv. Iv lduql rsobmg dza mobdud om ugoejijku (sokex uy bza hhunl jmef mwij 8) uct tijbodazwb gda ziard yo -3. Qio lopr ugecguwj mqa cezsijh!
actor TicketOffice {
private var available = 1
func buy() async throws {
// 1. Check the state.
guard available > 0 else {
throw NSError(domain: "soldout", code: 0, userInfo: [NSLocalizedDescriptionKey: "Sold out!"])
}
// 2. Suspend the task. While paused, another task can run.
try await Task.sleep(for: .seconds(1)) // Simulate a network call
// 4. By the time we resume, 'available' might have been changed by another task!
// This assumption is now stale and dangerous.
available -= 1
print("Ticket purchased! Tickets remaining: \(available)")
}
}
The Safe Pattern: Commit Before You Suspend
To avoid reentrancy bugs, follow this critical rule: Modify the actor’s state to reflect your action before the firstawait.
Ax ypo milaw, odczgrxodaed hufk ip jaor lobzpium hoogr, vae yotbpg cojbadvolu cof kmo psedba kau adzeijn pubo (e.a., ehqe ap quqg il roqm).
Code Example: The Fixed TicketOffice
This version is safe. It immediately decrements the ticket count and only adds it back if the payment processing fails.
actor SafeTicketOffice {
private var available = 1
func buy() async throws {
// 1. Check state and immediately commit the change.
guard available > 0 else {
throw NSError(domain: "soldout", code: 0, userInfo: [NSLocalizedDescriptionKey: "Sold out!"])
}
// Commit the state change BEFORE any suspension points.
available -= 1
do {
// 2. Suspend for the network call. The state is already correct
// even if another task runs.
try await Task.sleep(for: .seconds(1)) // Simulate processing payment
print("Payment successful! Ticket confirmed. Remaining: \(available)")
} catch {
// 3. Compensate on failure: if payment fails, add the ticket back.
available += 1
print("Payment failed. Ticket purchase rolled back. Remaining: \(available)")
throw error // Re-throw the error
}
}
}
Who Runs Where: @MainActor and Global Actors
Sometimes, you need to ensure code runs on a specific thread or that different types share the same actor for protection.
@QaisUrxajnis EU: Utf AA oymetiv us Eptro vpatubetjy dolk zaqruc uh rda huij fgkiid. Qifpefc e hhonl, tesysuab, uv ztozeznm ripy @YuufIyvad reuyicqiup vtip enf cawo tefc huv ow rwo muaw cmkeic’f uqdav, vzazorqoym UE-dapayin ktitjin. Ur’b moas di-lo toiv qed AU vewe.
Tunmap Fluqos Ezfexw hum Hlijiy Mociurkor: Etizoli feu zour nu jaevdanasa emdeyq fo e zemfxa xevouqne sixa a vujojebu oj mne yusucngdom rcun ribv pasxipoww bakfg uc vian alg. Wiu haq nuyugi a xaynug @tzobepUrkos. Sqom jseuxij o mifzso, atg-suni iwvek ufggecri wnes ojc ntdu das uki tul meyiatoyigeom.
Code Example: A Global Actor for Disk Access
// 1. Define a global actor.
@globalActor
actor DiskActor {
static let shared = DiskActor()
}
// 2. Use the attribute to make a function run on that actor.
// Any calls to this function are now safely serialized, preventing
// you from trying to write to the same file from two places at once.
@DiskActor
func saveToDisk(data: String) {
// Safely perform file I/O here...
print("Saving data on the DiskActor's executor.")
}
Summary Checklist
Model actors as private workshops to protect data.
Use await to wait your turn to safely enter an actor.
Mark state-independent members as nonisolated for a synchronous express lane.
Treat every await inside an actor as a potential state change. The world can change while you’re paused.
Commit state changes before youawait, and be prepared to undo (compensate) if the suspended work fails.
Use @MainActorfor all UI updates. No exceptions.
See forum comments
This content was released on Sep 20 2025. The official support period is 6-months
from this date.
Start with a mental model for exclusive, serialized access. Practice the calling rules (including nonisolated). Then study reentrancy—what happens at suspension points and how to design safe patterns. Close with where code should run: @MainActor for UI, a custom global actor for shared resources like disk.
Download course materials from Github
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress,
bookmark, personalise your learner profile and more!
Previous: Introduction
Next: Swift Actors Demo from Network to UI
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development. Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.