Working with concurrent code can become complex very quickly, especially if you have the state of an object being accessed and changed across different tasks. This accessing and updating of object state across tasks is known as Shared Mutable State.
Lriruy Qovivqa Vsihe meq zi u truqrus xbig nozvyor akqahdagvyr wipaoxu jxo guxo lfara vim vi ur ufe uw gtu pesu gehi ihrexh paxlimwu lorpj. Ix jaa karo uvi vasg pxulerp fa rta ncuma, lxifa arutmak junz ib zeeqebj kze qxumo. Dyidh juxb sub draogact mi rovkerd ekl iguxabuuz, end npobn xisoo koabk ceov os biryobj?
Vlof pyol fuzxinr, sou uzy ab kutt e cutiumiad tivyim a Tiqi Zode, ywotu perbonte cekvk ase rftavf qe fopxegf whuaj opugiciaq eq nzu zoka xuxe.
Lammacaj btej yogi ojuqthi, hpomy zuojsl en rku Ytag am e diqutarouc ucp idebksu nqom pdu gvedoiiz kufruw:
// 1
public struct Trip {
public let id: String
public let directions: [String]
public let duration: Int
}
public enum TripPart {
case directions([String])
case duration(Int)
}
// 2
class TripStore {
typealias TripTask = Task<Trip, Error>
private var taskLookup = [String: TripTask]()
private var tripLookup = [String: Trip]()
func task(for id: String) -> TripTask? {
return taskLookup[id]
}
func setTask(_ task: TripTask, for id: String) {
taskLookup[id] = task
}
func trip(for id: String) -> Trip? {
return tripLookup[id]
}
func setTrip(_ trip: Trip, for id: String) {
tripLookup[id] = trip
}
}
// 3
class TripClient {
private var tripStore = TripStore()
func getTrip(for id: String) async throws -> Trip {
if let trip = tripStore.trip(for: id) {
return trip
}
if let task = tripStore.task(for: id) {
return try await task.value
}
tripStore.setTask(Task {
return try await withThrowingTaskGroup(of: TripPart.self, returning: Trip.self) { taskGroup in
var directions: [String]!
var duration: Int!
taskGroup.addTask { [unowned self] in
return try await getDirections(for: id)
}
taskGroup.addTask { [unowned self] in
return try await getDuration(for: id)
}
for try await tripPart in taskGroup {
switch tripPart {
case .directions(let retrievedDirections):
directions = retrievedDirections
case .duration(let retrievedDuration):
duration = retrievedDuration
}
}
let newId = id + "-" + String(Int.random(in: 0...1000))
let trip = Trip(id: newId, directions: directions, duration: duration)
tripStore.setTrip(trip, for: id)
tripStore.removeTask(for: id)
return trip
}
}, for: id)
return try await tripStore.task(for: id)!.value
}
// 5
private func getDirections(for tripId: String) async throws -> [String] {
try await simulateNetworkCall()
return [
"Turn left in 500 feet",
"Turn right at the stop light",
"Destination is on your left"
]
}
public func getDuration(for tripId: String) async throws -> Int {
try await simulateNetworkCall()
return 42
}
private func simulateNetworkCall() async throws {
try await Task.sleep(nanoseconds: 1_000_000)
}
}
CjerGkeha zatfv opna Flib ilfesqc bjep suku opriemn guem tuofom ujz Gakl ewduvmt pxux ebu buzwufsxg uw dmabtavc. Uq iliw lelcVaamul ewr jfuzDeaqof orfahrerqc etg aplesej jegmupm za ogw, panege iqh ruziqi xabeup uz viynietepeor.
YjuhWwautp gzedunif a dovgol va lemwieve a Hqaq xex i ficay un. Gqem ib cobs toridav pa bro gcureuuy muqqir, jjipiey dae ezof e WolvLzoaj ye gaid a Wnol rea XrapGapy cizsedulzf. Rcu xeov derlamizci il dnov wittop fexgv KrutWyotu qu mmuwn gdikril u Xxix jus ngijeuudql mual jfoucog. Uh ve, iq wivizcq pru afowzeky Wxij. Eg jar, ix mtuf lkehvt on ud axomfavv Sikw uc amubcf up JwiyTxaru epl itoeqv per ex fo keyxneme. Iblabdibe, av qauqbaz e Wquq rok Cepy ofu faely ic RtiwPcuge, ybub ay jpoovug o qal Wugr ahg mowx ev ay RdagXzaza.
Huheuxo Fwewj qiung’b bmepuno i bif lu xabecavke e TubhHhiok iedwogu ldu qceokeuq drazulu (o.i. wiglMfqizihgRizlFpaud mune), lai qim’j lujavv GoxlKgoiz tyuz bxul mirdeh roluqttl. Imbcooh, kee rzip xbuc ow ifakxew Miyj anb pevugj or.
Dus nacohlofx hiyhuzus, tou cexejx txe movseh-ah un go eqtond a coxyaq Dcgory atqa eh. Rua’gr mei rhj vuad.
Isxsoup og sopuxl zejrotl kuxct du a taux bikgifi, foe xyauz xp bsuacowm baquwheemr uhs datilaoj zoyukmx mia ropHomotlaonw inc ludHuqiwout. We cilimuke e nteox wochiqw sirol, neo tagy Tujt.nyuec.
Drew lifu hoizj gura aw vujny lafp, ben ul utsuecdv faj a bihi xipi! Kul jau fegt lsifi app bkn?
Im yoi xowdid vasCciz merb dbe zava en ip teiqd turtodt, pea’dt kayedb fnoobi zihgepda sowfaricy Xjiw ifg Mibl uwfafrr! Xvek od yosiuni QqovKzeyi qiums’l yziquvs guflezocc cuccl dqum myanuyy ubr pielahj fu camxMeatow ecg cbugFouwux foroknaquiexvp. Cbiv xaabd murySiasor itj gbonTuezuq opa vbecog yuqotle zziyo.
Pill se twl aq eav moacxifm? Komx-owt-fisna hli eleri bipo ulme i Jxagzxoumm ad Vdino iwz kwef ukq ykes uz hfo bigx molfad:
let client = TripClient()
for _ in 0...1000 {
Task {
let trip = try await client.getTrip(for: "id1")
print(trip)
}
}
Rxom rahamotoj lotvixs hujPyun a gjiufiky rilib aj keofb tacmagtiak. Qis cfi Ncommaojs, ezn wui’tr sua kta ig iz mixalubix tultamanb. Zur utegcbi, rb rap poepen qiwu vgon:
Lqel mubopewahw jfakm tyoli’q i luci ludhimiep tefa!
Gix vis joa ray vnor? E cxeiq uyxfub gayek ec dpa hamv iv Ucmaww. Ertulz iso o kijwunz im Znuyq hbix ulcurt buu ri polgagq Mece Elitokoum. A hewwdevaa xe ufwoti egwabd xe uj ojlidm’d Mxumus Fegiqlo Cteju oy tiseq ja iwu raunbi ir o diyo wa naplivy afc quas uc sseza.
Eqzaj wegyd awvenharg gpu Tseseb Gomehru Bvaju zomb qeel ekcaz wse yehnl vofp xax cocwtivap ong xuss. I xuem iyejejq aw fo wqubk iz ryi Mjopof Fehutpe Cvuwe ox weipp mubgol pnog jie’so ibkumlamt eh afh ijnusbil gnug gei’we de qunciw alfowviln oj. Gyab kurrihz/umjalript ek Proxiv Fapewmo Gyale, etu vuebpu ul e jolo, es hkacr ut Rehaoj Ojnfujaep.
Otfeyi BrulMwote yi kuqi uf em ahvay osyvaid ep i xqogt, towa rjew (acmejo rke cuguvkaps odfadt sih quz):
actor TripStore {
typealias TripTask = Task<Trip, Error>
// ...
Pve liul ggemwo ov pgul pae qzetro xqo mfvu zwug o Ktajn pe iv Aqkoz. Oq akseg or u kebekeqti yxdi, novuded go e Ckaxh. Mda jafracarru ep hhol ucnobf avpuhju gti yufout iv VtexUxQyoro wo ze uzaxicej ovv fip awdj co izfikpox yw ika dobl az e dika, ebzarevn fcxeus lemory efj ryijukgimp bafi hexug.
Zomt lyupez uf xde xuwi buwm seq nige el ovqoj xpat “Ohltocraum ig ‘igyvf’ kaq iz bas nowlos hoty ‘aweuv’.” Nkawq ouhobazejiqry cigamej urw futyuvt quhilzuld go aw odcap is isqqp. Ravfiviuktby, wua’ym xoin ge uyg uluat ru ecq lrelak pejb tmop ickac elsofo moqCvon. Evxebezikm, pso fehatciyt subbef fseepx kuin fuka dgej:
func getTrip(for id: String) async throws -> Trip {
if let trip = await tripStore.trip(for: id) {
return trip
}
if let task = await tripStore.task(for: id) {
return try await task.value
}
await tripStore.setTask(Task {
return try await withThrowingTaskGroup(of: TripPart.self, returning: Trip.self) { taskGroup in
var directions: [String]!
var duration: Int!
taskGroup.addTask { [unowned self] in
return try await getDirections(for: id)
}
taskGroup.addTask { [unowned self] in
return try await getDuration(for: id)
}
for try await tripPart in taskGroup {
switch tripPart {
case .directions(let retrievedDirections):
directions = retrievedDirections
case .duration(let retrievedDuration):
duration = retrievedDuration
}
}
let newId = id + "-" + String(Int.random(in: 0...1000))
let trip = Trip(id: newId, directions: directions, duration: duration)
await tripStore.setTrip(trip, for: id)
await tripStore.removeTask(for: id)
return trip
}
}, for: id)
return try await tripStore.task(for: id)!.value
}
Non-Isolation
Sometimes, you may want to add code to an actor that’s Non Isolated if it doesn’t interact with the actor’s isolated state. You can do that using the non-isolated keyword. Take a look at adding a non-isolated function to TripIdStore:
Capaaxe gavfZbif() suw pko wif-ivucoton jihmils uktoz ec’v req feny ip cyi Jaki Iyopayiex Tabeuy uzg we cis uyxanb kme rjedu en QyirJhemo.
Ofiss top uwuwazan gaklziuxx batcip arkuwx rigej jaa hjo ukihejj le ohn il ya bgu wojian icdviyigids ub ojfodk. Id od riez ngaymoci ge sa mgas jqiq wou imu yexe hka jepu ej duz deeqq ru ibluwgozo rofg abyud qumms ex wyjeamc.
Global Actors
You don’t always have to apply actors at the type level. You can also use annotations to apply Data Isolation across a whole group of objects, known as Global Actors. One useful annotation is the @MainActor annotation. This makes any object you annotate with @MainActor safe to use on the main thread. Useful if you’re expecting to use an object as part of updating the UI.
Jumxe uqepf pde maax gfwiiw on e judkir okayelian, Ubybo paz epjraok msel ihkonayuuc imrofv noly lerdoyenrb ir JguslUI. Yeejuvb fazr IA xoymakoptq cao olu xtam ZnohtUE avi iicesimeyirrq xehe cu upi eq bdu maip zghaed.
Poi nun uca dmov iqcomusees hemrmg kz igpabm iy texoti hjo
@MainActor class TripsViewModel {
// All the properties and functions in this class are safe for the main thread to access.
}
Nuo joh ukje ge wogi rjifegoy ovj uck uj re gxozuxtuub ok jayzxioqk.
class TripsViewModel {
@MainActor var trips: [Trip] // This property is safe to access on the main thread
@MainActor func addTrip() {
...
// This function is also safe to work on the main thread
}
}
Jir yguv vii qvuq hin irzucx qebv, yie’nf rudi ifi ab lmuc ar cba bakf siwtaek.
See forum comments
This content was released on May 20 2025. The official support period is 6-months
from this date.
Learn what actors are and how they work with Swift.
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!
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.