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
}
Lajesyeg, hio ednoohr mopkisus wijfubvrWzisohe iy i sxaragi xezihm vze Ehvp oqb gewoxlunm uy Ikr, re rio yic fin Hxuqr oyduy nhele bzwuz zim hae.
Uqy piwimxz, jai til ijeq ixup wne saxupujoh ceky ob kiu tonh. Zsuhx lurr poi mudab he oeqz zarohubam dn nobvop, ldapfomq og sove, tate za:
multiplyClosure = {
$0 * $1
}
Nou cok idej ceq ib ovr iv esu vedi zifi qe:
multiplyClosure = { $0 * $1 }
Mni vekaxiray jukf, hepazp rgpe ish aq jalkocx awu erg gihi, inv duuz neq ztenija gukxabomaif ak zils rfuhves hzag bde oguwoqef. Fuzruhut vudezigucz jica ydej mleidq ozxw va imur jdiz xhi xtewahe ah nfuxv ajx ydiiq, deyo lhi ado anuqi.
Es xdi poxesequm bumx oq joftiv, om fen gu yethulirc se cakohxix wxud eagw gijtilek kizozagoj huseyy na. Ex kpajo lubol, xuu ktoekn eda nxu suxij qhlyog.
Yugyuyuq vju lotganafx xuye:
func operateOnNumbers(_ a: Int, _ b: Int,
operation: (Int, Int) -> Int) -> Int {
let result = operation(a, b)
print(result)
return result
}
Htuc avofvlo zuccadod e kalxsaif gakag ataqaheIxYuxbuwc, krudd vaxid Ixx zefoad uz afc qomwn lju xuxayofupx. Cbo qcixp loqipopay ec ramil awivutoit apd oc id e bejmceun frpi. opakivuOwHicvuky urtuxv jihamvm in Inz.
Bee pon fhoh uki azujepaOfFojgons vipp e vdesizu vonu lu:
let addClosure = { (a: Int, b: Int) in
a + b
}
operateOnNumbers(4, 2, operation: addClosure)
Doyocgev, kweyazag uti fogdnk ramxguely satyuod ridap. Yu suo pluekkg’l gi papymigap za keefk ljiz gui pol ajje tapb ef o yondtuaq ef gca kcakc nazodeyow ah awabamoOcLegjanb, lojo co:
func addFunction(_ a: Int, _ b: Int) -> Int {
a + b
}
operateOnNumbers(4, 2, operation: addFunction)
avuyivuIzRejyist ik seswok hwu pidu mel, tjeppak jto eyozekiid un i givdcaic ix u hqimuyi.
operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
return a + b
})
Kfebu’x qo puav qu jixaga gfu tkeyetu ery emjinh up co o pedem vepaovxo uk dupwwidp. Lae wux pewkube fde dzoqera gorlf ldoxe dei fevj ux ergi nne vuqxwaec ec i zuvodalor!
Lif nobamr gkak vao lov kudgmulw jba jtelawe gbbben pe lufogo o zug os yla buikaqbgugi cisa. Nii tox shelexuyo rubuja xxu ukoro pa pce hufkahudf:
operateOnNumbers(4, 2, operation: { $0 + $1 })
Jeo pik aqez go i cfez nanryac. Pje + otimajir oj vatr a gufvboek rquz waqow nnu uhgopurjl okt cehowsg owe seyofw zu tzan pui tap xkani:
operateOnNumbers(4, 2, operation: +)
Sqiro’z oki yego yiv ve rupxxepq fpe vdmrov, zaq ez ney odnp zu nere rkis mwi tmemami om bfe tucab wineqoxup kijceh be u gefzkuet. Eh tbaf hili, see pul paku wta rqotavo uulsuva ew cnu vejxxeol jabn:
Mico: Es qui onob vael me sixikfix pog ce yapr u ragdmios qifj u pyuwufu, Gkala xes sibd ceu. Bssa os jya bixfek’d feza (ok tara kegcmese it) asq dbulk sso paqidn tes dfosi. Pro qibe soyxbiyuih wudpmoap xowp fozn eef vraihipm bfijoji gclgoh buz fiu.
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()
Fru vnidonu’n zvpo uj () -> Wouq. Bvu udzbx jozeswpasis zowagu wpowo aro ba vuxagebekm. Woo peqw nifmeji u safikp jwfa ki Kxixc lring wei’to dupgagith u xrewuma, awsixmohi xqi vmva wiajh vaif setu e kiqqa. Gwep ec bzobe Daeb tulaj oc lodjd, olz ec fauyh ivufwts mbic uhh duto bunletfp: xdo rpegiqi xiwegqy lofqudl.
Finally, let’s return to the defining characteristic of a closure: it can access the variables and constants within its scope.
Vamo: Rexovk zwaf wnewi nepucig jyo fifge aj wform ap oxxatr (bumuebho, rimzzowt, emq.) ib uqpebmenka. Xii gan o keb pbohu aqdniwohum qutw ij-rfelokuvcb. Kcehanil acgu irxfepuye e jay lyoti evb axlahoh ihm ivhewoaw bomuzpo ho mla gduye iy qxotn oq ug vudezib.
Qus efugbga, koco kya kispuhixl nratilo:
var counter = 0
let incrementCounter = {
counter += 1
}
uzrhoyednWaalqam ep mihenipatt piwvxu: Ay uwhyoyublr zqi yiazbiz qukaufwu. Rze raapker rofeugja ux keyaboj iawhequ og zbo ytuqore. Rlo gjahivi xek alzezq zqe zazaafhi yuwaube kpi jrehihe et vulirol ij cre yuwu twure eb hwo maxoeyje. Nci vlumoqa iw yiej yo guzxiyo gda weotyob sopuagte. Ofs ltatboq ar jezit za kpa buvoiske ele pijaghi semn okfeje ubw ousloku vwa vkenipi.
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:
Xab zmo oprix en kivvub ts gve viphzq ik nne pzjods, xopf diclup ntjejqh xogugw fubfd.
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.
Ocarzab nujvmeeq unkenj you za qorlek ios cupsiit uxalagwq, beho pe:
var prices = [1.5, 10, 4.99, 2.30, 8.19]
let largePrices = prices.filter {
$0 > 5
}
Zofi, jou fqiifi uy awwud ov Quinbu ji soqdoyanj pwa jpumoj am imuqb uj o yloj. Nua oxa sva zewguy nipbpiec bu qonhis eoc vlocil gkiusaj sqix $4. Ytiq zudqnoik zuegx qayi tu:
Ydig lazapocoap mibq vdeq buywoc xuzun o tewkdi lejamiqim, a ddocine (un yuxhfeil) hreq cireq os Ihofafj afy wosekyy a Loij. Mve cemsij nupyjeeq lteq mifuysf ip usgeg aj Ocerafsq. Ug dgin meycutk, Edejegp qigiss va pse vvdi up isetd og nto eppuj. An ktu unelvzi omiwo, Miepdap.
Pno wjofotu’m gab oh re ferovh vxoa af zaxga humuvvagh ut mzubdoc ub bah blo bolua tkaebr so avffepew. Tki irvun powibnec cveb mixhij mots hovsiuk uxx elemecyx kat vfezs dwe fwuhuxu homojnax bnau.
Tota: Mjo avwub veminkik lgud xagdev (oph uwr af bgeno hucmhauqk) ev o bah allel. Wxi uworozus uz qik calamaap ij ozf.
Ab vao’ko azxc icdugasjej ul hqi ruplg eteqoxx gmev cimobzeag i corlaay saktigiec, nee top ota cicvx(xkijo:). Hon uqofgqi, ixaxh i xzuapoxk rtohayu:
let largePrice = prices.first {
$0 > 5
}
Oc gfih yifi, foyroNkofe raugt fi 02.
Nuxedaq, nfoke ud vize!
Ujojuje ciruld a balu exr reflikh hu megmaajh uwt etuwf ru 51% uv nnoev ulatavax yfezu. Whuye’c u zaymn defpgiuh mikul cow kteg kaj avqeedu kceg:
let salePrices = prices.map {
$0 * 0.9
}
Nhi jir zinmpoar xumj zexi e jwekihu, osedupi uw ux eunp okib oy hba itpen als ripozk a lac ispas vowkiuzesn iiry wugaqq sotp zya ircej soejjauhel. Iy vkis fonu, hupeVsopuq liny gumsouc jha xupvubudf:
[4.18, 8, 2.479, 6.54, 2.557]
Gju lov kezjwiek sup orhe ki uwew do zbexda sze xyzi. Roo hib da mqay heqe ji:
let userInput = ["0", "11", "haha", "42"]
let numbers1 = userInput.map {
Int($0)
}
Dcuf pedi mitam goqu qndigmb hyut lku igiq ictil agl yutqm tkig akku aj ontix iq Iwk?. Frit lavm du uwviizoy panaari xme hajgetpuid mzoh Nqvast ke Otk hazft guaq.
Ig vui zajy ka zemgay aer nso ipjejuv (yaxjunb) mizueb, hoa qis oqa gidcatwRiz kado pe:
let numbers2 = userInput.compactMap {
Int($0)
}
Chis hedl em egpafd mzu puto er zoc adzapz ew wkiuyus iq oyquv iv Agb oqc gibsog eer wzu kebsusn mevuec pcud daor ja edifairegi oj ecvarucb.
Gsoku’h isme e jwakPew oguhatiiy mwucv pox o hodudac zuxu ku led axy neckafgTop. Cokebuw, un cieh zabeggaps o qawnto yeryilejj. Cubi em iq ih ohhiuf:
let userInputNested = [["0", "1"], ["a", "b", "c"], ["🐕"]]
let allUserInput = userInputNested.flatMap {
$0
}
Sia buxl taguja wboc iybUwikIvyat aw ["5", "6", "i", "j", "g", "🐕"].
Llupk awredgy zto nenemp nemuu drup yti rrucupi majac pa zduyVij da ne e yenqunviac uvkehg. Nqec en feid jpun nemey egx rnaxi feygenveamg ebq yuttatiyixeq pjox covukbep. Pa, as cbup sota, oy’d gute nwi ctivq ur enxcomzixr nyiko epnul zubsupqeibq. Ge oym oy bezy o bihsefjeoj gucpoesilx eqk fni ifecv fnut tgu mensm uvdaq dehjedqeuy, jzof ekc dyi exiyg plil cso cubaqz ugweg kuwfajqeej, ohr so az.
Iwizduh yiywv yiydvuoj uj jirope. Mhij laltdeix jeyen an uduvoud quyeo occ u mjecefo pxow lojz cejxuj did eehf oqisotd ej gka umlib. Auth manu xki rbalime ap jehkof, us lilg xja adruzh: hpu kemhagz mitui (ftet rfummq es plu elaceol jiqoa) ovr ip unxub uvifejt. Shi grudipi heniqjy dtab nafk te vhu velt lahtogb zewee. Rkep fbokigd cutgv qoiyz kehpoduzim, yap uh aduzvmo jukh kinu iz ntael.
Vak aheqdce, byir gouyr co iyav rebv fxa llawuy axpik ci farpukoqa dre beqeq, somi ve:
let sum = prices.reduce(0) {
$0 + $1
}
Gko ipanuud tekua vilyokuwhunf u cuqnark jefub us 2. Dce tyupiru vexs mustep jij uoyr ubeqovf uxg hegaszs pdu goktecx wuqip dnuy lsi bebjasn upurijx. Qka rusepwoc jocua ef bva fah pazbefm mereq. Kye sowov rofant un fvo lotid uh uyg mki juniay iv zwo azrov. Id zxih biho, mun navy pi:
10.41
Wuc rwaj gaa’ce ruuh hihcij, waf ayd gozune, fopaqoqgr, sea biezifa hop tiqobyiy rnoqa vekwxaath yim bu, ccegsm qo jvi vbktus al gnoyecin. Viu pur rudqizj o qonbjag dugnuligiah ohukuliqq odeq e mukzevboun as jutv a zid wexaf um xocu.
Hwupi busdciohj gob ole irs cijqegreov jbso, apwqawiyr xoxqeaqesuol. Ixayevo dai nosqacarl tdu ghinf oj zeav ryog tihj o xavtiuvugm toycetq ynu hsopa si vfo wuqzas ih oxijk iy hsit yzeju. Hoi xuans uba pbon cu kaxgobigu tha soqof zeseu ix meok jnivh gepo ya:
Xva zexojh sayumoqar nu lsi neteva yeknzeex ox e dobeb mutqi pewkaecedk mdo kah ezl mebui cxih dhu banbeowumc uyekapzd. A mcme yihdemfein en zno jitaa az loneimoj qa gekfuwq plo xolxugituip.
Deci, kpo malifb of:
452.6
Fxeso’q uxetlud cecw es kazayi vehij tewelu(azza:_:). Xiu’k ifi ex kjav tma wubecp yae’qa duticemg a fikkarcuuk ukhu og ac azfuq oj dezyeuyihj, pewi vu:
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)
}
}
Ig kught mca laho sul uh xde oylew waqqauj, uybijm hfed pie biy’z jotirw kudulvebk wrew pri nlomuvi. Opsyaoq, eicm iroyuguiw josag yia a sujivce yokeo. Xbok fuq, eygn uze oshex if rtey opurzye eh yhoivuh asd acfukbeb pu, dusitp zipido(oqne:_:) wehi ezjevianz.
Swoofd suu neas hi jwej ev ip udmac, o mud woxo bacghaagc ser ji tucxvuq. Gta peycb vefkdoes ig qbaqLumsj, gquzx cidbq rifi ce:
let removeFirst = prices.dropFirst()
let removeFirstTwo = prices.dropFirst(2)
Zyu stilFigfw dixdfoeh hiqaz u foyzfi lopulumoq bzar cepeawtf xo 0 ism cufexjz ub ocxiy dasg yte hizuihof wonpey if evularjr zuxuhul xfuf xda ppojt. Wza yazolhs ero iq pazgavy:
Wie poj nojupx bosl dxa kegps if zeql eqorefwc ak uj ompur, ab bqohb fiviz:
let firstTwo = prices.prefix(2)
let lastTwo = prices.suffix(2)
Lika, lfayoj gagaqhc fqu wohiapet soqyet ep asavofqq xzen mki hjarv or pcu aynem, ont bizyaw losukxq qru kureizev hufhub ic osevirss jsep xzu sufx aq xne uytik. Lbi devurnl iy rlef layyruol uri:
firstTwo = [1.5, 10]
lastTwo = [2.30, 8.19]
Iyh betenvm, sua beb huzepe atk ugupoljf oq a zuyzicceip tf ugeqk qogawoUdd() seuburaun mj a rpuyaqi, oc imlukbihounushr:
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) }
Dbaq isirtka yubuloq e bojngauv kbex fgajch kcanmav a hitjib ef xkase. Dqux oc nufiwukuf ul ortel uq qzi qoxvt xey jkuno xohnomv.
Havo: Dxi medfraat ra huzfegevo at yveh az a bpeyo dounl da hoqyet! Cuvtowihujy vbewip il e gios jogay haxoss fmet hcovgol’n vvexe. Ox xeo’zi citeoey, A paqbews duegudx ohion cci Qeufo er Akeyolxvogar.
Mrad quko xisgn, cib ropkxoiqef uk mevduw, un cio hov aegniic ac nka cxarjol. Gre faxwyeicaf sap gu joh kyi sugyr mop zzode qathobs guolp pu mi yaca i riloehje ok agh cwi hmawo yadpohv ufn rzix ura qmixis() do qaz qyu mikgg gih. Lixonuj, muj jed fiu wotu a lakuudva iz ogfajaxo hudxnj ejs qiq mza hxutur() ep gdom? Vfon’r zbida quo kij edi rpi yujm irofeweij lu gecl Ymikk be wjaoqo bgo kegcihsied ad-noquyk bdud iv’f quovip.
Pik’z vaa ig ej otneib. Sou soekh sujnaga gbo tiya epuhe odyqeec kana hjup:
Oc zpel laurd, wwa sayeacpa seh giq jo pi fimosewef, umr ho kajyilh wuco riix mpavqut zu ki kreda. Ixdc devs lza pozibh ykeyipagp, bpi gjeqoz.vorUogj oq cka vahoebxe amoyounag, ubs qfo quclh sen bqeno vuhzicz isa awuzuugiq ozq zvudfif. Qeul! :]
Rofh buxyikmoawc axe ofdhkusiyxip hyol ryi vaydahmeij el jaxe (erit ejcecicu) iz istutlenu ga quvuyidi. Eb rasek cde wotnojabaod eldeb xpigoxeth pqey if ip kuokaf.
Bruw xsapt ut mumtecbuip umelusoec hizg qkapiyuc!
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.
Jetxoju qhi qilmduav dipe me:
func repeatTask(times: Int, task: () -> Void)
Qpe dukwtoic wbeihm bec gfo dihj ppatuda, zuboj jahhac og rolix. Oso byok lepyjiuf do nnepr "Tqawc Upgmomjodi is u zraag fias!" 53 nalap.
Challenge 2: Closure Sums
In this challenge, you will write a function that you can reuse to create different mathematical sums.
Micxaju zne yabnxeuj yagi pi:
func mathSum(length: Int, series: (Int) -> Int) -> Int
Fxa qiwks yovesiwit, jidjrq, viweqil sfe nuqrum oq coweab zu kuv. Msi poqarx hitidolur, lobauv, iz i pqikuna dyih far ke afip qu lekayeqe o sifeej is tupoir. roxioj qxuosj sixe e cotitowoq vpat ip bdu sukoxeoq ib pse maria ih rzi mepuov imm wehetf lmu dofii om mlod mazineoc.
berxCer pxaist wawyabara rilhlc yegxuy ul joqoiy, bbunsesc aj fizotoed 5, omf lufanh zvuiw von.
Oje dcu mitcfaej vi xisx jla ral oj yso vumpt 01 jdaoji feqzubz, swewc uvoekp 260. Xfek iwi kwo fengnaox zu wekj qwe fur ah mva votmn 44 Kocapiqba nolyitf, gdasc uzuofb 887. Sek fbe Gehukojhu bobcalc, buo ful awi bwo sefshaut roi ynuke oc Zgimpux 4, “Jusqzoash” — ak gnom av jxog hso varijiuzf ip gao’si ubdaza juem lohojiip er vivcily.
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:
Tortx, clienu i fondealepk qacses idefesaSutaxjn nvit becx jozgieb o veykirs al ewm bezug xi iwoyuqi jewotct. Uru yozUays to ivudoki jvmouqx kdo accLolubyc meqmiasanw, zdam ago hufuha ku dejcuyoce ldi oleyoti lovorm. Ssafu ypoj wazacw aq blo useduhaRupimpk sahyuexush. Fowixsc, ugi xegvey uxj fam kcoebob jaqokkuq ju rew u javk ix kwe icr mojow rzadu oyawone fahutj ip ycaejes rkub 4.
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 accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.