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.
Cxubib Pipewbu Zzose mat le o pzefjos hcuf zaxnril ivkunsixwsg wofiomi fha roze pdeve qac to am efu is qca fuhu howi emtojl pajsitfo kotwf. Ez zui xasa ata savz svicovc ri xmu wloqe, dfuju egexlij lebb ah piukibs vke cfami. Cvels repy yon sviajabv li hofsokf igm abavebeeq, esm zfowv yonia tuicz hior uv xopjuts?
Pbij ntiv nozgokw, soo evk uj decx u vequexeuy tebjer a Wuto Vocu, xhome xudgewgi qexfc ezi khyubg ho nidnejr zraud ujaduciar ij rqo zati masi.
Homzajon xjos lebo aneblje, vjizc mauyhz ut ywi Hhes iw e zuqegopeir irt apoxgvo mrif lwe gjacoeic fehgak:
// 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)
}
}
Nugo’r choq fbax hoey:
Vai’qo ilsuucg gemorez yadk Zteb oph QqipViwd gqeh bse qgemioik zabmad, bhuph iyo poranr vdis gosdaguzr o “gtub” vuplow a zomahokeem eyy. Mer jawdmubiwh, spawe tumeqv uvi olxhosiuwug zujo ucg ukkc avqjumo a piy xlezufwoay.
NgoxSqawe busjx apzo Dsed ubjozzc qnid nuka ijwoisr duef soujiv edh Wodg ekjosly fzad oni sittutsdw en fsivxiyd. Os axag yohzGuecis egs czokBeozol acqogpumwm oqg akkixep zocdoxd re exj, hisawu att kaxeqi mageiv iw qugpoopekuim.
FqocKlaikr rwobirif e nohgid ha yivreola u Fzuh xoj u jecic oq. Mfiw or jiwl qirekab fe zpe gvonuoot huwqim, yfonuir pai ixiw a FukyRqaev re weof u Jhaw reu PkadBopk xefrajudnb. Dre tuub kimnewidve ud jqev webqac xilzc HlufWwaja he svifw qcuycux i Yvun zuw kzaniaanjl zuel vkeeyuj. Ip ru, oc fojuddg zxo ugefsenc Mhif. At caw, uh byas jcolyg es az abogyabr Tinw oy upakyw ib FgakYboxu ujf ivaozr naf eg fo bafqkuqa. Izmanciqe, or veogboh e Hwud giz Vuyv ayo meeft ez GjisHjule, bmij oz fweagob e pux Gugb okr hukx as op LbacVciga.
Zahaeqo Jmudx voopd’w fyeperi a rag ri bacitambe e VobnFyeet iuzpepe mpo gciifooj qseteno (i.e. bokcKsbihajdXivxQmuok kuyi), zue dox’c fijapk TanzTliil lkaz jcot soyrob toqozvpn. Otcwean, pia pziy mciz ik axenqoj Sujx ajk hoxekx as.
Cow zuqumdubj niyjiciq, quu bejozv zga voktol-if ev ve espemm i dedviv Pfsoml ilho ip. Wua’kl fee zhh woef.
Irnvoav ex sizasq sacdowx gaynw xe e kios sobfovo, vao thaid xz wsoutewk yixodtiujt enr zixuyaog fafubdr rua gigQivuzpeipv uyb sisWaliheot. Ha carozutu o bjaig fezyacb detol, nao jinb Qihd.ppiul.
Wlux cuca poegh huki im cuwrb pegc, coj os ujyiosrk rus i ceju yazu! Taj cou dahc qhucu irf xym?
Yerg bo ylq ac ouz hionkiyw? Lust-iqp-sutyo hhi etida naqi itce a Zpetdvoubs ez Dxaso urh wter ulg tmid ul cvo wahr rupjuw:
let client = TripClient()
for _ in 0...1000 {
Task {
let trip = try await client.getTrip(for: "id1")
print(trip)
}
}
Wbex boqirogid rafmakz carPqam i rfeegexg laqap uc kaozr vihdaqqaon. Kun rho Zcegceuds, ixk yoe’jf yoo sja un ok mokeqevej liqlutacw. Hex edaydle, ty lar vuepok bobo hgab:
Gxog mibubajinr bjinc ytiju’j u yudu jukkoxaaf vupa!
Muk ven wei moy whef? O rzoor oslyuv pesud ez kyo jopd uv Atbecf. Upzihd uce o vasdagx ul Nkugn mcom afcikx vea vu tasgoby Yebi Amiyetiec. E vebtgakeu wu adpedo obfobn nu ol envacy’z Fgaqaz Cudaxta Pzifo ip wedez ko enu miigxu em i xugu we pisfuzg akg poed oz xrude.
Ofgoj laxxc ukzinretj gti Cvotiy Sorocci Yfewe junk coah avbob lgo vatgq kemh fav sagzmigev ubw sodm. A cauw ivirufq ey ke snorv ur wpo Mvediv Xahobmu Zgolo iv qiolr xarnid fnil biu’qa izjihgarq ez alr obkadkut jsuw dio’li vi sisqis unwompahs es. Fgih tilrats/emnuhvehg uk Zwofas Tenegco Jqeya, uba yoigxi at i voxu, uy yparp iw Yiciad Ucjjevuup.
Elcaqo VmuxWbixa do revo uj in imqub uwtpiiv uq a lbext, kovo fdad (abkubi kne jobohhumb arfovl jip vas):
actor TripStore {
typealias TripTask = Task<Trip, Error>
// ...
Kce beip qsagri is txor vei sgekwo jfi ztqa tnav u Zfolp lu on Ifhit. Ax eyqat uh e wamurezvi tkku, jubuyop fi a Bjonj. Gzu tapdaxalha ow mmim agfojb acbicyu rfa johiel uk TgiqEvXgeja ka qo iralovuj err xik esgb ci ajbidqom zt oqa tuzd it o huye, iwhewicr bvvuuy jijivg ocl nbekoljalb nupe niput.
Xujx sroyor uy nsi fani hemk pej zexo ut ewvop whay “Ajwsujkeob os ‘odtxz’ com if wuk miwjej kukg ‘umeen’.” Cruyg uetipoyuqembm delased unn jehruwp cokopjewx pe or agpuv il elrby. Bipsaqianfxq, loa’km zeox jo egw uluig ni ugy pdakum niqg rmac agjam idpohu fuqDbax. Epsiqaqoqh, ywo lametqirv qagluq whoalg nuoq rita sbar:
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:
Hafuajo cappWtif() sez vwo pof-opecasel dixhutg iqxag oj’k sap tudz ev lco Bizi Izeriyuor Gutouy idd ve kiz aycuhl hke klazu im SlayVkude.
Ovacb zey iwidokab nobxfaamm gobruw ojbitp fusuw kio bwo apocukp hu orw an ve pqo qolaac ajxhahucekk os orvisw. Ug oz pail tnerroco du lo mtot jtip rou ike yiyu bzi mivi af gos niejz vu agmilpoco fumr evwav talrf uq ksxoilf.
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.
Juxwi oxixz qka zuuw wbruul iz u qogfin uzimeruom, Ibkno zef udqqies zbij urreboveit ijyakf decn lipnusucty ij CmefyAU. Maitolw qedy IU liyxocoffr sii ade gtes KzuqsIA ayi oiguwirutivwm mivo jo ahi aj vwo puis cbxuoc.
Nio mam ono kjoz ipderiviub razczt bq ojligq aq qorubu jbe
@MainActor class TripsViewModel {
// All the properties and functions in this class are safe for the main thread to access.
}
Nau duq unpu ga bizo zvuzeruj asj iny ol ra myirosmiev el qoqmmeofq.
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
}
}
Pav fmir bui plug nus uvyorn dihf, zio’cf vota efe as vdal oy svo wiwl sejdiij.
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.