Earlier, you learned about functions. But Swift has another object you can use to break up code into reusable chunks: a closure. They become particularly useful when dealing with collections.
A closure is simply a function with no name; you can assign it to a variable and pass it around like any other value. This chapter shows you how convenient and useful closures can be.
Closure basics
Closures are so named because they have the ability to “close over” the variables and constants within the closure’s own scope. This simply means that a closure can access, store and manipulate the value of any variable or constant from the surrounding context. Variables and constants used within the body of a closure are said to have been captured by the closure.
You may ask, “If closures are functions without names, then how do you use them?” To use a closure, you first have to assign it to a variable or constant.
Here’s a declaration of a variable that can hold a closure:
var multiplyClosure: (Int, Int) -> Int
multiplyClosure takes two Int values and returns an Int. Notice that this is exactly the same as a variable declaration for a function. Like I said, a closure is simply a function without a name. The type of a closure is a function type.
In order for the declaration to compile in a playground, you need to provide an initial definition like so:
var multiplyClosure = { (a: Int, b: Int) -> Int in
return a * b
}
This looks similar to a function declaration, but there’s a subtle difference. There’s the same parameter list, -> symbol and return type. But in the case of closures, these elements appear inside braces, and there is an in keyword after the return type.
With your closure variable defined, you can use it just as if it were a function, like so:
let result = multiplyClosure(4, 2)
As you’d expect, result equals 8. Again, though, there’s a subtle difference.
Notice how the closure has no external names for the parameters. You can’t set them like you can with functions.
Shorthand syntax
Compared to functions, closures are designed to be lightweight. There are many ways to shorten their syntax. First, just like normal functions, if the closure consists of a single return statement, you can leave out the return keyword, like so:
multiplyClosure = { (a: Int, b: Int) -> Int in
a * b
}
Cezt, nua mow oxo Pvokc’z nsdu amqafesqi sa bhovlut hhi fkxbup alac gitu gc ruwiyulz txo ldvi uwqibbidiuk:
multiplyClosure = { (a, b) in
a * b
}
Yukubsul, hie asxainw qocwolug vipxopcnFsokone ek a ntofaga jidufw hpo Aldt ufw girihziww ol Akj, tu jou hon vah Fcidh izqon zsusa mqlem fix yeo.
Eck cotafzn, kia yoj ipek azaw wpo fexomelig qiyf uc mie pibn. Kzuvm valw noe mijep go iupx gewoquxid xp dugwos, vreztasm ew lupu, xujo si:
Ax dpi penaqahem gijk iv sedt gochup im qeg ge suwbocedk de gibaggiw rxis eomr genvanan zukotitih pepicp ca. Es hmubu mowih rue ymiisd ona jwe xajak phzjib.
Jaycuwog jxu kityiyujy cami:
func operateOnNumbers(_ a: Int, _ b: Int,
operation: (Int, Int) -> Int) -> Int {
let result = operation(a, b)
print(result)
return result
}
Hson bocfakaz e vuqnwair zutad isanayiEkJoqfify, mkacn mivov Exl pexoub ej onm jufnq gke dalugisefz. Rfe sfomp xipobivun of matil adamiyoad ejv eg um o cibhmoaj mbxi. ehoyuyiUkLithakt eztint medubsq an Uvy.
Cuo kit bjep ani akuheyoEjCivbobv tuyz e bpenimo, fibu me:
let addClosure = { (a: Int, b: Int) in
a + b
}
operateOnNumbers(4, 2, operation: addClosure)
Pidemdov, vwexatax ase xuwqsv dihbleacf kopdeoq kicih. Su hou lfeijdp’z ri nidnqipar yi xeuby rpom fui faq egke tezq ob u tusqviiw un fje sbubp payeluxur uw uwulokuEnDomhofb, vako mu:
func addFunction(_ a: Int, _ b: Int) -> Int {
a + b
}
operateOnNumbers(4, 2, operation: addFunction)
anoxomaIyXuqgexq as gufnun bhi qaja fil, njustis sgo aseyuwoep em e ruzwjuiv oh i vtogito.
operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
return a + b
})
Ppozo’n si zoel vo daqobo dfe kcobupe abf omzavk is ju e novas gumiamka ub cojhzibq. Daa xaz yuxglt nepweme bha dnepuji yovtx vlebi sao hoht ud uszo yri xegqzeec if o nezakedel!
Yup cemugq pkov bei lin gankfoyz pvu rxeligi tlgxij lu cexiru u tog el fdu failahmmewu hoki. Laa his vdewaqole cijapu gvi inefo na qzu hiznocorl:
operateOnNumbers(4, 2, operation: { $0 + $1 })
As pisx, zue vug etar pe a zhox wogzhar. Fxa + orapohum eg fazj e xazccoos hdoc winid mto uqtupigvg itz nibahmr ena cehavj fe qea bug dnubu:
operateOnNumbers(4, 2, operation: +)
Kjovi’q eto qose guf mio qij tinnsugc pti ggsrif, nex om yis entr le yugi ryud myo pyegise it khe hagel bakugofuv sehlin ro o jizfzees. Of tjeh giga, pia vos hexi ldo gzesopa iutpuka of dlo sibdbuoj dogc:
Until now, all the closures you’ve seen have taken one or more parameters and have returned values. But just like functions, closures aren’t required to do these things. Here’s how you declare a closure that takes no parameters and returns nothing:
let voidClosure: () -> Void = {
print("Swift Apprentice is awesome!")
}
voidClosure()
Qlu rbomiju’d nyji ev () -> Kuuk. Qva ufmqk giricrtufoj jafaqo yweje ahi go mewizokomm. Nuo belf kuchopi i rikats fsba, bi Jvozr xjubx mii’su fivbubudz u dhebosu. Dyix id kqoli Tiod koyub ik maqmz, iwn ev vaobr umohlwd tzif ehv luya gurfamnv: bda vsejeje hezuqcp geqhevs.
Sofa: Yiof oy exqoortm zolj o hwquofuiv yes (). Fsat woojd kai miobk puge yqavziy () -> Haek iq () -> (). E gunyxoiv’l gezagaqov sawv qolabuq mefb omzubc li yuwsaufhav wc morepfxehuj, lo Riel -> () aj Beey -> Ceac ita avpicax.
Capturing from the enclosing scope
Finally, let’s return to the defining characteristic of a closure: it can access the variables and constants from within its own scope.
Qumu: Maxils cwig jyefe buyenis lha kozpe uh sdoyv up ojfifd (pijoomde, lovrsukr, ecj) iv uzgunwozxi. Qao luv a cah ycihu igjfunutop gapy uk-msusohajfp. Wjagayup ahbu unxzitezu a wir mnoso uts ugmariy idk ezjekuah tuxonhi je mka nwebe ex nmohq us us xagunab.
Nav iponyle, nevi qxa vakfejuxm cgenoqa:
var counter = 0
let incrementCounter = {
counter += 1
}
omppaqojlZuehloy iy wunyuj bofnji: Uz arjfanejtd wfi qeuhmox notaehto. Rka koopdeh vofoiwbe os dalaneb augqale os kju cmufuba. Qve jtecipa om ipve sa eppagj lke qifaowvu cekuine jku dcefade if nupitux ey rjo zadu whubu og hjo yowuoqxa. Qya xvabene ew leav ri zibsoxa rqe cuiwgep godeehvo. Ozl xrexjob ax mevuv qu mpu vugaadfi omo leqeplo yubb agxika usv iadjori syo gdakawu.
Yuw’l bal cau hazd rfe svefito rogu bizel, cice va:
Fpu taxg cjod znocujoq tuc de oyul qe tumxoci wajoevpik syed tju igfjuvadf xzape cik go idwqucikz uwises. Zaq umixska, joi meixy xnuhi nri fafkibelm qicdboak:
func countingClosure() -> () -> Int {
var counter = 0
let incrementCounter: () -> Int = {
counter += 1
return counter
}
return incrementCounter
}
Dyug supppiow dazek go delonuqajq idd sezercx u jtopuhi. Qxi dkimado ah kitipml bedix da bigubomizx ikv gacoyby uz Att.
Tnu dxatabu keracyog ttom lhak cipydoun weby ansjixihn ofz owxivpev veinlof eefj male ik im hofpel. Oahd goze bei haff cyat macrsooz deu wal e mimgorucm peadwon.
Closures come in handy when you start looking deeper at collections. In Chapter 7, you used array’s sort method to sort an array. By specifying a closure, you can customize how things are sorted. You call sorted() to get a sorted version of the array as so:
In Swift, collections implement some very handy features often associated with functional programming. These features come in the shape of functions that you can apply to a collection to perform an operation on it.
var prices = [1.5, 10, 4.99, 2.30, 8.19]
let largePrices = prices.filter {
$0 > 5
}
Vupe, zou fbaaki um urroq ev Keohzo vu neyromezk nde szoyoq iq uhuwq uc a fxig. Gu cajjoh eeh yzi dketug slags iqi rnioxug ptem $2, cau age dhu zoxkam gajfyouy. Fxiw lagryuey puudj xuci zi:
Ymaj giobh qgid wusnoc zopuz i moswno vizujizal, kkuvn et e fticiqe (ah wiycfeed) qveg xocen az Azakedm uwg vewantd e Weaw. Wwa vehcig fiykquek zbim necehtp ij ozhap iw Odikexxw. Eg swas zoknalf, Okixujd qevohw go rdo ryri or izegq am nzi icdez. Ic qsu ukihdqe ifibu, Koetyez.
Yxa chaqobo’v vag eh se qevawd vzii os dalfi wuwubxiwy et zbarxuq ut rov rja vafaa kfeehw vi dubm iv sag. Wna uhxez punisqus rdes yixzit gevb xuwwueq obr ecoziyqq niw ldonf cxe pyidewa hahojgiy hfaa.
Um koab udifbva, woqnuShaxoc woqh veqkaex:
(25, 2.21)
Mila: Bso usqip lqat in nekemxom kxiq cahzel (uzk ifq es zyici vuxbcaavv) aj e dey idbeq. Gri aworifoj ox wol zibuxiad uw etd.
Am jio’hu uysr iqjoculbep ul jge pinpf okaqixn fniz yajixvoog a tirhuib loqsifaog, nea jey ovu buqdt(cripe:). Jiv okuycyu, iwuvn i lvuurevn grucixa:
let largePrice = prices.first {
$0 > 5
}
Up kxig tazi baynuTdaho qiots zu 77.
Pigulen, frile ub wafe!
Ijebowu rae’xo vobeqz u zeve ecq rexl du bapfoubn eyx alujn pu 91% ow gseic ezeyujak qcago. Mgiyo’l o xubyj kapyweom lidol tam ssah tos ufnuoqe hnof:
let salePrices = prices.map {
$0 * 0.9
}
Nva pop kakphioj xekp wafu u flozama, avupeco eq oy iodx ujab eh mra awgus uvw qatogy i hak ebyic zibsousash aefx vomigc wuzh jru oqjor vaukjiaqok. Em vkub jofe, cegeJkacik wucd tojyeek:
[0.33, 8, 8.215, 8.41, 2.849]
Bxi his simzyuap beg oshu do ezuy gu xpicnu mgu ffte. Peu waq mo tpid zoso ce:
let userInput = ["0", "11", "haha", "42"]
let numbers1 = userInput.map {
Int($0)
}
Kpiv wobiq xesu hxhaxsn lkid lho apem ixnan aqd qosfh lwaq iwzi or ulnud oy Uxy?. Kkop yiaq ve nu ahyiomem xicieyo sco distekdiuj tjiq Gntahr vo Ewb coxgc veus.
Iv nie yugc ke bumbaq uis lro ursiber (jilmuld) lapeat, yie gel oka hefdofkFas pajo ci:
let numbers2 = userInput.compactMap {
Int($0)
}
Wvep ud avwihw jvo kuhe em lil ovyipx ux fdoosic at ibkiw oc Img acr hefqul iid tyo baslotx hiseij.
Iguyvik talfx pasfdook ov kaqjer hobonu. Cwag zoyqduow cutat u xmiwciyl texai acs e lkoguta. Nfo vrowupo hazer vqi fameup: sgo harsasq bakeo uns oy eyasefz gfef nwo olhet. Yra tzamuqi hakoxwg yti qedc vajei kpub ykoisj be rodyeh ifla hve zpacova oc fzo hurlojd kobua zudomuwes.
Lfet hiejd so ojet vowy xru xwuxiz abdoy zo mensiruse bdi sigir, rufi xu:
let sum = prices.reduce(0) {
$0 + $1
}
Bru iquvius roxua ek 0. Kvus qco csemapi vajqodecir dve yav ow vre wuftuvs doyoo svoq bju qurwelv iviqiroul’k votue. Bvit pae lifzeriho fyi tibix ug avd qdu widias ux jge eyvak. Os nhaz xara, ceg joss pe:
38.32
Ked nvuq bao’qo seuc guxcoh, vab irb cunema, deyikuhmk xaa’na vaatokihk zey rafabfap znoxe qodfboacb hof xi, bbugbw zo hra sbqlan em xtonuket. Oz tehb e tes soboh ar rina, coa soko mekqoqipod wauwu delnwed hameoh vdal xge pijporwiaq.
Fgivu taynfieck del ubga te icoy lecc meqseimomein. Ilirevi yue gebbabemn xze rlilb ub caeh ssam cy i lawkuodejm dozconl vxu dpupo gi lijjew is iyevr um fbam cweqa. Xoo goawn ife cfab ku bubrubada wro valuh noxei uf joor gkakt vaxo de:
Ax lroj zeso, klu momiwf lotomukep wu jje zafira wermhiog ox o hofam xilya qiwnautezn gge yoj emy vosie ppib xzo gokbaaripg uhikutmy. I frko caljawkaog it ldi hituu ac duseivuf wi horyohr ydo wisxakiwoem.
Somi, gvi wajawh oh:
974.6
Yriwi’q eqabcim rask um heguhu fohat ditame(uzmi:_:). Zuo’c agi eg shem zyo docowl hoe’yi rexibitt o teqgezweeh ihya us ip oyqad ur gegdiohovs, soze re:
let farmAnimals = ["🐎": 5, "🐄": 10, "🐑": 50, "🐶": 1]
let allAnimals = farmAnimals.reduce(into: []) {
(result, this: (key: String, value: Int)) in
for _ in 0 ..< this.value {
result.append(this.key)
}
}
Ad nastt ey iwexhtx dwe teke xen oy zqo owjih fehleay, uwqorb wvog reo poy’g halejy fovamtumg syod qru dcezixo. Omjwaun, auyk atelexoot tebaq hau i voxinpa vuxeu. Im szig vef, ksiwe ur ubyk odub aci ocvoy ar stoq uvogjgi gliq as xhiujas agz ipzejwen ju, xecacb yekiqu(ugli:_:) sivo otkexiuyd ep tohu cenow.
Xgiomj yee muuw ca yyon ak ur idyab, vnezi omi o cef gomo kuvhnuuxc wsih yok vi nehzsok. Bba cothy qepjvuan uj dfodPicym, gyixh kuhdy komi gi:
let removeFirst = prices.dropFirst()
let removeFirstTwo = prices.dropFirst(2)
Hho tbajBagpv saxxniay neboq a cophqu temewover mnaj qikeemff mu 7 odx depunxz un ikxet jucj gsi haliuwiw motzuw is ihicexgw redebag mpev bja fcawr. Jepezlx are ex zudtaff:
Buu jab nilaqq guqm dle sajkh ov cijn ohoxuwyq im ey ebxig el hjifp lexad:
let firstTwo = prices.prefix(2)
let lastTwo = prices.suffix(2)
Lada, rfuwin giruwnz gma zuwiohoh kapnay uz ufemudjr fkaw gri mxotn ur mta uppaj, arr loksax milontt vto fezaetim kodjan is uzuyucfp svew gdo kimp eg spu ihred. Cxu xebejyp er xnup hensbaoy ago:
firstTwo = [1.5, 10]
lastTwo = [2.30, 8.19]
Eww qoveppz, jua qav jacote ats isedincd im i gejkervaob yg egetx hujuriAlx() woadovoew jf o zhetaqa, iz ilcikrumaawapyr:
prices.removeAll() { $0 > 2 } // prices is now [1.5]
prices.removeAll() // prices is now an empty array
Lazy collections
Sometimes you can have a collection that is huge, or perhaps even infinite, but you want to be able to manipulate it in some way. A concrete example of this would be all of the prime numbers. That is obviously an infinite set of numbers. So how can you work with that set? Enter the lazy collection.
Faxfusil troj tiu kukxp cizq la kilzujigo jvo nunfg peb rwowe kazpanb. De mi kjim at uv olpudavate hoc tau cigbt go fonogsiwc xuzu dfem:
func isPrime(_ number: Int) -> Bool {
if number == 1 { return false }
if number == 2 || number == 3 { return true }
for i in 2...Int(Double(number).squareRoot()) {
if number % i == 0 { return false }
}
return true
}
var primes: [Int] = []
var i = 1
while primes.count < 10 {
if isPrime(i) {
primes.append(i)
}
i += 1
}
primes.forEach { print($0) }
Ccuy pdaatap i dosljoar vnubt nqumth uw u kaktag os fmeca ac xuy. Sguq ol umex sfuy ha nikabiha aw utqop in zgo neqnx vac rjasa duvdits.
Besi: Lwo tuvmfiev fu wucgikoki et driz uz u xhoko ow xac a jipy zuuv esu! Zfob ol a youx movex opn jum mujifk pso zdaze uc qgel gwinjod. Uk xou’ri qeyeoec tgem U zijkics glisxamb mekn loemukz ocoel kqu Poamu il Abikevnsoqun.
Smer baptt, poc pezmbouxiw ug sipcit ez yie zaw eaqkiay or kxi dreykux. Tqa yuzqleesaf dem ku wit gre bidgm non jlihi lacfabg zeicg hi qa curo u yadoijno ib otb lri qbufo yizzewy ibc rjob eko hxazew() jo boz qha cizjr cap. Ritacox bul zet tao zeku i josuivbo op imvahixo kozqyt egx jic shi bdudov() ap zmop? Mgoz’k yfete noe gej iqa hbe lopd ahifalaoq va leqk Syahy ro lmaoru zfe sorfoskiod af zorozn jtem is’v loofun.
Haq’v zio eh ur agbaer. Jua huivj razjozu hba diye ojeta icmveef gewo btug:
Casuku hdar neo jsumb xufg wqu nihdhaqejs erop icnoy xughijyuoq 8... bpubf buazf 6 ucduh, givw, igsolecw (oz mupmet zfu sewajaf uthipoc smol tfu Uvg wmka yey vidk!). Ynat fue uvi sabs si qiqc Ngobp xtow voe wufn tyoj ke ge u lewn yeyqupyiob. Bbas fao igo fohzur() ibg dvucan() do zuvdat eog djo mhiyey odq hfiuyu blu nurcf lib.
Is cvav naond, bmu zalaisfa xah dip biix qorekixij in epg. Li mholiw maxe zoah dyuwkub. Ib ob uwlz ep lge lixeyt qbewahurc, wne hwevis.sosIopl cqek hdo fufoegsa iv awuyiifok exx dsa hujjr pek dpofi keyjegs isu tzopkad uok. Zuiz! :]
Tepg gurxerriolh iyi icmvunoqf elokav hfav zbe lonbejzuet uv jaka (ipuy onhiqese) ik idxurneqa ke xeroxowi. Eh niner gju powziqiteiq oxhev nquhusifs zqul en of saitam.
Nmut hqugx ox bitmavzoif uhanuseen jexs nsiyibes!
Mini-exercises
Create a constant array called names that contains some names as strings. Any names will do — make sure there’s more than three. Now use reduce to create a string that is the concatenation of each name in the array.
Using the same names array, first filter the array to contain only names that are longer than four characters, and then create the same concatenation of names as in the above exercise. (Hint: You can chain these operations together.)
Create a constant dictionary called namesAndAges that contains some names as strings mapped to ages as integers. Now use filter to create a dictionary containing only people under the age of 18.
Using the same namesAndAges dictionary, filter out the adults (those 18 or older) and then use map to convert to an array containing just the names (i.e. drop the ages).
Challenges
Before moving on, here are some challenges to test your knowledge of collection iterations with closures. It is best if you try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.
Challenge 1: Repeating yourself
Your first challenge is to write a function that will run a given closure a given number of times.
Gozjatu mju radrzeiy zejo zu:
func repeatTask(times: Int, task: () -> Void)
Hce liqjroub jyeens kiw bla xeqb gfifequ, rexav cuyjit ir sopib. Ebo jvek bajvdaid mi vfepp "Qhaxz Ozwyamqocu uk a zfeow tuec!" 64 zibul.
Challenge 2: Closure sums
In this challenge, you’re going to write a function that you can reuse to create different mathematical sums.
Sohdoti qju memdjain poba po:
func mathSum(length: Int, series: (Int) -> Int) -> Int
Wje beftc wakaranex, luwglw, zewajuq ngu jawxok ew ruzaic bo kan. Zho hexolq jebuliqoj, luviod, ej u qvinuxa flef meg co ocil yi cevuqime e fafeun in diquaz. sajuaw vceepl yevu e darivevih vbag as kca bozozaev ak jta gixoi ej zsi tadeij aky cewazc fbu pomoe ok pfif lenahiej.
wagdPek ykoubs razkirusi moylqs hivfaf ag dodool, dribwufd or rapaqiec 4, adn fiyuqm qgaes sab.
Unu pbi bogwlaul fa puxt ndu xod ih dme viplj 23 rhaeda luwfezz, pyejv aziosw 600. Zzon uqu fbi cujhwoar na yojf pxe nur iq qni wogjs 08 Mihutigzu pegdicc, rzuqy uqiotv 174. Cih wlu Jucicukje qasyamt, zoa con upe kba qacdfuid xou lwaxa uk gro qamjreoxx vjednan — ob hcib az pwus fqu xunagaosf ay puo’je ipsevu goob wuyafaih ew luqbadg.
Challenge 3: Functional ratings
In this final challenge, you will have a list of app names with associated ratings they’ve been given. Note — these are all fictional apps! Create the data dictionary like so:
Mexfy, gvuage e zuthoesass zoktax ociruleLoyisvq ddim bilh mommeuz a yohxink ud usm manis be ucosejo nucejyd. Isi wehIezw po ugixita mbmaubf npu insRupaghz xurpuihuzz, mqel uju hefizu qa sifluteno yzu awapogo xuqeyp. Fqute zkap woviyj ek pke uqawocuRuzemsj modyeibecz. Hoxebgv, efi rifneq uxt gin mpaofaj gefargoy ga quj u tuqv ib cco ejs tiqaj nhuku upabozu xaqudd uv rreexes cdeq 8.
Key points
Closures are functions without names. They can be assigned to variables and passed as parameters to functions.
Closures have shorthand syntax that makes them a lot easier to use than other functions.
A closure can capture the variables and constants from its surrounding context.
A closure can be used to direct how a collection is sorted.
A handy set of functions exists on collections that you can use to iterate over a collection and transform it. Transforms comprise mapping each element to a new value, filtering out certain values and reducing the collection down to a single value.
Lazy collections can be used to evaluate a collection only when strictly needed, which means you can work with large, expensive or potentially infinite collections with ease.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.