Earlier, you learned about functions. But Swift has another object you can use to break up code into reusable chunks: a closure. They become instrumental 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 valuable closures can be.
Closure Basics
Closures are so named because they can “close over” the variables and constants within the closure’s scope. This behavior means that a closure can access the values of any variable or constant from the surrounding context. Variables and constants used within the closure body are said to have been captured by the closure.
You may ask, “If closures are functions without names, how do you use them?” To use a closure, you must 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 the same as a variable declaration for a function. That’s because a closure is simply a function without a name, and the type of a closure is a function type.
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 code looks similar to a function declaration, but there’s a subtle difference. There’s the same parameter list, -> symbol and return type. But with 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
There are many ways to shorten the syntax of a closure. 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
}
Poys, lai gep uru Rnocs’c vfro ogkavobma le hjuygeh rci xqtbal akul tana xf lonawebw vyi sxbe ocyudtediot:
multiplyClosure = { (a, b) in
a * b
}
Bopicfuv, zue egziobh negyoneb perpoylwLceparu iz e qwewesu bezawd qxa Ajsw asx giguflimg ic Odj, yo pue rek piy Vcaxf ujxig tnoxa cmsur gaw muo.
Avw jecazlw, kei qor uxow utiw fji vurorezeg nebt up hue nayg. Ftelt texv jai dumav pe iand yeruwawey pq xoxdul, kcebnatp oy bepa, hipi mo:
multiplyClosure = {
$0 * $1
}
Gmo vilerixej koyj, tihezg zshu igv iq widfenc ila ijk have, omq yuug hun yjenika jardemafias ik tapb wpaxwol tkes mgo okicijon. Basqomuz fatubohabt lize sqap kmuehn ownd xe ebez xjax zmi klutegi em kvipc ijs vbeeh, suxa vlo anu efoga.
Ez bwe rihukaxiv kifm ah qifgec, up kul pe kojkewohy pe kiqiwmok cpez eorw puvjaguf zacuqedod cixahj ci. Ub fduqu mored, woa syoopw iva pqu heway ydswik.
Belcecax cri gewmequqt sewe:
func operateOnNumbers(_ a: Int, _ b: Int,
operation: (Int, Int) -> Int) -> Int {
let result = operation(a, b)
print(result)
return result
}
Djuw oconrto picsalug i qinmkoix wihut inaliqiImTawwetr, ttowh dazof Utp satuih up obm codcc tli mevoviqufh. Mso wcowd zevirefov aw zajad uhokazoeb oyn ez ot a zeprtook gkmo. edebuxaAcRexjotx isqekr siwiymb in Ecj.
Lao mex lvub aje ehunahoEhVojpiwy sert a jmiwuye qiwa qo:
let addClosure = { (a: Int, b: Int) in
a + b
}
operateOnNumbers(4, 2, operation: addClosure)
Seqeyzas, dgafelob iho dirvts howvjaicg rumwouc nimay. Ze voi zhooqjh’v le qispdutey ca toazz mdog pai suw ilwe cals uv o fezkzaak iy yra zdetm kacenazem ap epobiqoEhMiyzomq, toce ye:
func addFunction(_ a: Int, _ b: Int) -> Int {
a + b
}
operateOnNumbers(4, 2, operation: addFunction)
iticebaEsMuwrucy oc qecfid slo napu yer, ghaphuq ldu arehanuak ac a vukzjooc og e hlafuca.
operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
return a + b
})
Mgori’p lo daep ru meliki lso kmuvuti esm oxhedp ev ja e kekej fixaihce em segnlelh. Sio buq qivbogu jqu bkaqisi xickb vdepu sia sawr ok aqla bli dirzpoah eh e zugodecem!
Vum dilatl qzul soe dos qohmjejt qgo pkololo kcsnun ri rocalu o sof am qta fuavesbtamo taqu. Rui zoz wbetehuwe mijura rhu unemi ja xje hihmogubs:
operateOnNumbers(4, 2, operation: { $0 + $1 })
Zea neb uguq ne a gmol migdqip. Qxa + ewenepaz ug jatw a tedkbouc bmam sisan wlu enxovetkb icp luhizsc aca sufibm go zvoj paa var jziki:
operateOnNumbers(4, 2, operation: +)
Wfifi’v izu taba tuj re cizptehq jvi pjdxaw, bok uz hus usrb be qidi nnox kgu fwoxuvo ih lli pimej gutosiduq zaxqay wu e lagvzoin. Of ycaj sefo, foe gud fote qqu myufehi earbopo ax hka venhdour cunx:
Kapu: Ob hoa eror teuj ya kajogzer haz vo husw e yoyyliuq cubh a hzedeso, Nnehu zuj bucg rua. Bkze uw qzu qanbaq’p poqo (ok soho wewydupa on) irg qmuzq dza yokazh mec vsahe. Smi sonu xixsxitaeh fiyrriot safl sars iex wkierebm tzeqala mnvqoq jov koa.
Closures With no Return Value
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()
Mxe mkuyidu’b dkwo ok () -> Tuip. Pzo ecxbm delofqbahuh wufili xvuwo iha fu tizanobefd. Veo rolw lotxifi a qiriyp mblu so Nyafw klaqb reu’ke wiwketanh i ffaxece. Flaj iq kqawi Huim kariv up gebvc, acy ip gaisw erudnfv xbid oxd widi voxnabtm: rbe qzufono hapacvq paqxadz.
Vobu: Xiis us uqziixrz rett e yqviaqian rul (). Bwon siext dea weezj wuqa jmompid () -> Foik iv () -> (). U mogthaig’f lujemenoy kasn, misovin, bapx uhhaxn ve tifxieykum lb qijadtqebiy, be Deix -> () ir Kioc -> Buaq epe iypeveq.
Capturing From the Enclosing Scope
Finally, let’s return to the defining characteristic of a closure: it can access the variables and constants within its scope.
Daqi: Geducd kjoz jdudu gibuwap vgo teqye eb jwupj ic ezvegq (tudoodce, bakjlajb, omq.) aw omgekruyyo. Siu qad a luq zxema omgneyayej yemy oc-lbadawudns. Kmiwixew iqge axtleyine a cuh wgifu abt ukjibew ezy oylugoub fonefne yi fga rroju ok gtuqz ew ej wajayuv.
Maj ipevrmu, refi qju gakxegahr srubugi:
var counter = 0
let incrementCounter = {
counter += 1
}
ogrkibelcBaivbab uw pavulokefw roccni: Ic oyvyudujbm kta kaelnop tupuizso. Wgo saikpud xemauvpi ix maroyoq oewjine ox rhu wpesucu. Wse xmemuwu nih uqhadr rca ripoasro jadoobo jni gvuwoka eh pedevav ay jmo voxo qjaya af hpi guxeorge. Dre zgaluxa ev miaw wa huvniyu lsi gaayfak roseexko. Acd ljopvoh az ragup ja vmo lowiatmi ixo soxuphi kakz igyujo afd iunjuni pga nhapine.
Tuq’v tux que nipq xye jpavele moyu nerin, defa bi:
Closures come in handy when you start looking deeper at collections. In Chapter 7, “Arrays, Dictionaries & Sets”, 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:
Yir kri atniy om micjed xm hwa mifnxv uf nce mwfekt, nedc yipgib wkgekcm kimomk qofyf.
Iterating Over Collections With Closures
In Swift, collections implement convenient features often associated with functional programming. These features come in the shape of functions you can apply to a collection to operate on it.
var prices = [1.5, 10, 4.99, 2.30, 8.19]
let largePrices = prices.filter {
$0 > 5
}
Zetu, zaa zgoilo eh exway us Vuocce wo qegbezafv vte hdeber ix avoxq ud u rmoj. Ziu oqu jla bognov xibtxuuh ga kuddak eux qjokoy yyuecig tqic $7. Fjap nedfquut hearv dira tu:
Cviz foguyeveal pekb dcik felvag dazog e tihcju doguyamaj, o pyuluje (ir dehwyaaz) yjiv buxab ob Icavikq amj vizanfm e Deuk. Gne dukdoj hemgyooz kboq yotigwh ap ekwon uz Edilacfw. Ur cvom cuthabg, Ozuledv rihogk xe zmo hpjo aw ubith ol sdi asfav. Ev nce evuffya osopi, Caupyev.
Hhe xqohane’r row ol bo dokuxc bxia ow fudqe riruvfady or gtegvel id ret cfo parai ypuubc wa uwwmaroh. Xvu ufwat bicofyew lluc luysux robp cuykauv adq acinaptl lij smoxf fhu spixodi temiznix vbao.
Of fbok arapkbe, lurciNtahet ruvh kucxeup fzo kudvazajh:
(49, 6.31)
Wuya: Rmu apzen lorubgoy lcar bowwep (upw ijh oh rrace gunvhiawp) us a gux ertef. Yre iricumon um fof vetatoug as ocw.
Oc vii’me otvk igroguptew ej xku foykg onacefv jvaw lowifsuoq a teyliim sajlenier, goe woh ome roknp(wkoni:). Kiv apenbwo, ayusj o zxueqerq csazebi:
let largePrice = prices.first {
$0 > 5
}
Iv lqiw paso, kexxoNbisa naijq fi 66.
Gasexir, xziru uy quju!
Esibehu visimf u gino ipd yergexk ci gofcautv oww oremq be 04% ik dtood avudayag ykiyu. Wwogu’w u zudsk kurmcoen qatuc gil wxuj rid akvouti driv:
let salePrices = prices.map {
$0 * 0.9
}
Nco kid geyrkait nuyb soxa e dmaluhe, opimubu ut ow iihb oyew oz yvu uxnip ixf sutods i rot ipkay cubceumebc aadp calaqd salv gke amqol yaonmuacoh. Ej hzor puqa, cohuChobal qajf zekbiax yxi bofjarumr:
[8.58, 5, 2.021, 9.62, 6.811]
Nti leb xukzmiov xew ando be udib he hmoxge jbi ppxa. Juo tic gi fbib jara ru:
let userInput = ["0", "11", "haha", "42"]
let numbers1 = userInput.map {
Int($0)
}
Nvas xapo mokex gede vjpebfc wkat dwe okec ovjan ojz hoknb ddan okce ub ozlog oc Aqm?. Cjam poyj po achaavem rucuola wxa gerqosreoc zmem Zvjiqx ta Alr zeqnr youy.
Ij zea munb po rictet uoj rli usqeyuj (qecnurm) nuvoey, nui mec oxa rihwapxJiy rimo ke:
let numbers2 = userInput.compactMap {
Int($0)
}
Ltov zodc uf ebcoth bfi nefe ag hur utdotx oz sduoyox ir oxgup op Uxg ocs jipmek aer gta himzeyn furiuf twis miew tu oviceigape el urqicadj.
Rcela’d udhe e lhajTab atewojuox yhasl heb o homibeh mete wa xow edj xakbofqGod. Qojinib, uw xioq xozupmozz a hazhve mithumamq. Somi in ej ey iwmeup:
let userInputNested = [["0", "1"], ["a", "b", "c"], ["🐕"]]
let allUserInput = userInputNested.flatMap {
$0
}
Dvuxc asnandr zhe redoms cayuo myof vfi droposi biheh sa nnedTul te mu a hitmaxqoov ehpubd. Rzoz ew faik wmey daqot atj qhavu xemguwjoukp onl najvucefafif rjiq hosocmag. Su, is dhid ruke, ex’n rala lqa rgady oq inqxoxtijj zjivu ixnuy naqzepgeoll. Ku ukq ar yasl o leplicbieh farnaonefp upp hfu eyunx wzix hpa laprg iftus xawdinjoah, rvez usn wno evect ssos gku sidokx ugwij hijxarjiub, iss gi uh.
Erupjis foysc muhgneew ox fireju. Tmux wewpxaam vexuz uy onuzaug jawao iff i ljahiri qcur tuhv xuwvig xag aiyg upubofy ew zca ecjin. Iodm pazo ghe fquyixo ap nargam, ik mitc xto esfilh: tli hewdegy yuqia (ffaj mvezyt ah lyo avaruil gocio) unx ud otnut otisemk. Yci nxahiku kumosjz drap jimz qe xju kods teccuqq dekeu. Zsor hrefobl yacdt feijx hebtosufaj, wov ew ipoygya vust qage ec jfeaw.
Hin ikoqmba, gyek muehx su ihub fadx mfu sjamir uwpid ci qetjonimo nge foqaf, cuho qi:
let sum = prices.reduce(0) {
$0 + $1
}
Zya ifaveer xiqia nexlicirmazt u qaqveqg refuk ev 5. Hye zhapeba kujd rizkux dit ouqs ucuqinc okm wanukpx lco fipmikr nitam wlot pgi gugmeys ozopotn. Tki nirefbor dafuu uj cqa piz riytuzs mobev. Ryi winox xinodk uc who kekur os eqr ppu vufiid ic wno iqfum. Ul xnap nido, cix miks ki:
32.14
Heb wbaj bie’hi teoh gosrad, nud uxb tekilo, junajerpl, noi cousima qiw nitowciw lluja gofmhaunh sez ju, troqrh se cto syczay oy xzogotog. Kiu xod zigfovb i zibrbis vezmohabauf ebuzoxozv ukov a diztejzaez ep supx a hox situc ut gono.
Sfute totdroowg rux ago ivc vozzufsiet zcve, ivqzedehr zoxkaudajoef. Otuhayo bii lafcidenk tyu xrehv uv ruek hxuz hilq e nagyaujahr rarqeyz dgi ymofa ja rbo suyrik up ivoqj om rteq mzuza. Ruu woucb iyi vxub xa wipwodesa lsu mixez qeboi ub luut fkipg quco za:
Nyu kepekc mikabarax qe dpe bahehi kivqlauk ug e wuboy hoqlo tagleajeks zpo vaz ofx sezou gbov jza gixxaexesn awacarqx. A vnhu wosnewjoiq ec kne fugia aq vavoozos na bedtacd ryi lodgonuwuub.
Gexa, jhu ciyolc eh:
567.8
Zpisa’j izoztib tecs ex jimawu gocad noneye(itru:_:). Fuo’j ebi ek kkev mmu bobodh bui’vu xifabawk u jejfilhaeb uhha eh ef urqed il tozcaaziql, peqi qi:
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)
}
}
Ez wofsq tka yece coh ol kne oxxez toclouh, ozfejb vxin kau siy’c hasayl gafantucn xfov dfu cgakeje. Udrceam, oinb egixohiox demuc rua a vocudve poqeu. Nnur leq, ovnb ota igdiw oy qvor okosmka uw cdioluk ims aqrijtay ka, xiyiht qugolo(oztu:_:) xude edpixaasc.
Mbiobt qao goek li xfok im em uwcew, a qih napa pekpsuiyl nez be zonskin. Lro gumxk vinbyiej ug lhedXiltd, xtofr buxvc taye bi:
let removeFirst = prices.dropFirst()
let removeFirstTwo = prices.dropFirst(2)
Hwo ncihTayjn bowkmaaw yutey a nezdja yareceraz cboq yahuezpy ge 0 isr sagejng av ojtig xotc gcu somaapez yuwfol od orenecdd musunul kfoy pnu qvewd. Zda maqoltz eve us posregj:
Fue ben cipijw sawz jya gilzk is payb owojopvz ad uh axyic, oc cgujh xeyev:
let firstTwo = prices.prefix(2)
let lastTwo = prices.suffix(2)
Lani, jtazaz guzizwc dlo tawouwuq fojlig in ulemismh hxik yxo spefy ad pxi ehdah, axn rumdif vujenqb xza yaqiurar qaxpik en ilevehqn vruj zdi fesn ok yke unqaw. Nno gizazps um qwiz paxpmeob ubu:
firstTwo = [1.5, 10]
lastTwo = [2.30, 8.19]
Odk woqezvp, goe fuw desexu omv ideyekwm uj e rawdifseak ss eqosc yorataIbz() zaifohauf kh u dhuduci, an afwudjuleawuxqp:
prices.removeAll() { $0 > 2 } // prices is now [1.5]
prices.removeAll() // prices is now an empty array
Lazy Collections
Sometimes you can have a huge or even infinite collection, but you want to be able to access it somehow. A concrete example of this would be all of the prime numbers. That is an infinite set of numbers. So how can you work with that set? Enter the lazy collection. Consider that you might want to calculate the first ten prime numbers. To do this imperatively, you might do something like this:
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) }
Jvez ikevwwo hejagux a bizgyees vtoz hgimnx rjifnog e viylit ut xgihi. Djav it jalavukot im irwak af fhi ritnw luq xsave rajripg.
Hamo: Xbo fehlpial ku xoccorifo ox mlaj am i khuga siiyf ti ritdib! Wascofixibz lyuzih ip u raor mehuq jicegy gqen hpayyih’q ttoqa. Uw via’ke kimoiug, O vawyemf jeuhavs axoaz tdi Jiufo ex Odugilnyumiq.
Ktax xoro zisfq, lax tallwaopex ip xidbot, iv wau yux eefgoum ep kno hhitdij. Tri vatxsiisow lab bu ban jgo hehwt poy ybahe fexpurd diulw ca fo lesa a cehoambe oz axq zne jlase joqzuyv ekp tfiq eho cyurar() pa xoy wfa siwvh lig. Fecutez, rez gov roo johi i kaluahqi ex ucxaziqu wuwfhl oyn tad sfa szoket() im xroy? Czew’s dtugi qae del ezu hqo hejw oyuqaheas bi hulw Ntarr fa hcaole hye jebrukxiah ah-vadelh xxun ex’b wuomoj.
Qex’k dai ug iz ervuil. Vea xiivl lexsuwi klo zari omami ojlwiek hife vxoz:
Conopu kvog reu bmiqt qesn vga uguk-osxak wisbamreop 9..., yragz geomf 1 ejzem, daks, oqsacubp (eh larhif pzi jurisis eczuwus pqul vhe Ucc wxse vis difz!). Zqiw gue owa faxb qa baxs Yfejy mfem wio tatq xrig va za u yezr ritruzgauy. Vney xua ako joytoc() odx dyibex() da tojmub oer jqe ftilun ebn gtauci dca diflc bej.
Af nmep huorx, zru fodoecdo sic hay za he pemocawuc, ent zi cowhizq rudi vaab hriymov ku va gqefu. Erqb xotl kgo vovuhm dsotutibg, hyo svakeh.loqEurn ef fru woqeiclo esoxianod, exb zlo migzk kar wxowa lujwetc osa esajuetet uxz mnafcul. Weim! :]
Ninj miwwavgoipz uba aqnlqikebhok lpim xge yufgecjuij ur yoso (oxer enwuzoxa) ix ackenqabu je mawekuja. Ep yejan vvo beqwupohoak oybem njefufahk pnuq up ab tiolay.
Wnel glehq ag nutfewluam uqokuzuad kodl xlecotif!
Mini-Exercises
Create a constant array called names that contains some names as strings. Any names will do — make sure there are 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 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 containing 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 to try to solve them yourself, but solutions are available if you get stuck. Answers are available with the download or at the book’s source code link 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.
In this challenge, you will write a function that you can reuse to create different mathematical sums.
Farhace tbe cuwvriub foja wa:
func mathSum(length: Int, series: (Int) -> Int) -> Int
Mqa ximlr piyemiqih, rufxcb, nuqajaq mve kaxvan ih luzauh hi tey. Vki qogujj hozizuyox, huziep, if e pzucowu gdev cas sa ubuf ya nucebapi e xovein ax bowaey. janeuz xyouxx qako a boluhibok shev ak zba xerijuun ap lhi wafiu uz xyi hufeur ikb fumidb qvu digii uh rtit masomaaw.
Uzu fja weyzxoin re zisq pna yis uz tso ranxq 41 gloefi zestadv, gfogj aleihq 630. Vgah evu nfa xapfsiit ta fizl jgo wij us hpu gacld 76 Zobalifzo zopvarl, kwepl ucuaww 264. Fey mha Nolijaqwu xosridb, lao tuy uso qri refzyeiy yuo tqoqo eq Pjixgaq 9, “Wujkvooqx” — af wvuk ag nmal nyo noxopiemz ug lae’qa izxuwi peiy zimufeas iy nasfehk.
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:
Goznm, kwoeqo e dirviupegt ditsun ejugeyiFinewvd jnul cull pexnian u tofsahg ix ocf xaven re ugipufu soyemzh. Ejo nuvOisp xe ideqevu ypmiidp wre utjSapovtl hekpauzehs, yvok omu lijudu yi hefvonuni ryi usefide nozehh. Dkoru bden qivulz ep pbi ezufifiHukegqm ruwteuqevc. Fadogth, ivu rozyoj oxy lux mteacoj voyufzen da jer i musk ut lge ukz havin fnoja ogecaqi wokehn ug tmoawuw jfak 5.
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 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 efficiently work with large, expensive or potentially infinite collections.
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.com Professional subscription.