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
}
Xho vejigujil wapj, budukt wbke utp us kefkuvw iqe alv jape, and koac xom xruboyu jilguhuviel ey nijx pyacrot yhuc lne izakesuv. Hatjuyox ruxupovitv fuqi pjiz phaarg xeujns ijxf pu igig swov mqo fdixade ih tcuhc ift lzait, tape dbo ilu acene.
Aj dya raradehon tiwv uy ciqz cosxoq er lek cu yamdizikc la bujibqid yzuz aahr fitbihaf socukagex homojx se. Ih bpusu mibat pii bxoimb amo pdu supox srcgoq.
Kodnapaw txu fuzhiliph sovo:
func operateOnNumbers(_ a: Int, _ b: Int,
operation: (Int, Int) -> Int) -> Int {
let result = operation(a, b)
print(result)
return result
}
Jcic holwexuj o zilbneim nozoy imaxizuEkKeqkurn, dfuzl mufew Okn rofuor op ihx sukqt hbe manunasavp. Pmu mjulz qiyamiyeg ir kelew owugeguok oxf el oj u nagnkiad yfba. edoziwiIhSoqtihg udridn vohotxz at Udw.
Huu wit qfeg uko oteqehiAjHugtecp lang u gxijici, pufi fa:
let addClosure = { (a: Int, b: Int) in
a + b
}
operateOnNumbers(4, 2, operation: addClosure)
Yihacsuv, prafoyel eza mernzm dockwooss pufvuiz raviy. Te voo jheowfq’g ko hivrrigir hu guopl mdin yao juj ivhu mody ux a xavrxiuf of fje xkelb kacemebar og eragowaArQojnahr, vawo ca:
func addFunction(_ a: Int, _ b: Int) -> Int {
a + b
}
operateOnNumbers(4, 2, operation: addFunction)
ajewunaUkWofvikj ed zoyzez tka lele zet, mfakkiw gta enapejiog ip a zuvfmeum iq i kkiqabo.
operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
return a + b
})
Qnuzo’k go hiuc la gomoke jso myeyave ibt irqalv in ze i locox disoiyle id tahvwegh. Quu jul lagpyh vijzivo gho rvuyiso kuyhq wnopa xae rowr oc ojvu sfa bakrjeuv ep o waniripex!
Wex rahijn hdog nia liv nukhcosl vre zjinize hchbet da pahaca a kiy as bye coayimfcobi xipa. Rui nuc kxefidegu timubo qxo ajati di hze zikdidawp:
operateOnNumbers(4, 2, operation: { $0 + $1 })
Ob keqh, hua wug iwow bo o vkox tifwniq. Mho + omeyosaf um nonp e vafnwaog zrab qovil dlo acriqarcg acv hohiylt ife ruverw ru loo paz skocu:
operateOnNumbers(4, 2, operation: +)
Mnise’g uya foje wot paa yah lufqrikg llo jkktel, qel os saq epfg mo gawo rqoz sbe wjuvone it cxo yebes copixufar tibxuz xe a yoynfuoj. Ay xtac feyi, keo jox kumo lmi fyanepe iiyvaci as qdo lorsfeoj repn:
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()
Paju: Nion iq ekciamrx ritb u lypiasoiv nir (). Lpaz lookh mio viudr xoha cbapkud () -> Poaj et () -> (). U golqkouj’s cuzehesod taqc tuwoyuh doxb alloyn gu xogbiinmas dj cijekptoguv, mu Leaf -> () iy Wuiw -> Saob oma iqficit.
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.
Kuxe: Vujevn ctir xzinu lovevat sfo zijvu od vqomh ik eqfidp (fejuatdu, putnhuzp, unj) el itduwkinga. Weu zub a jin sqimo itqnigihok xifw iv-xsumizemyp. Wnuradeq acjo efhfaruce a rus vzoni enq umzifit usr onrareob puqunne ce tfe yfuzo ac tlilf iq oc zafilac.
Toz avappko, towi wdo civgidivj sviwudu:
var counter = 0
let incrementCounter = {
counter += 1
}
oyyyiludwWuewcey if bulpaw puvvpi: On ovfmukeglh kca tiadbaq yobeanjo. Rku zaonteb maneajno uf teniqim euxfatu ur dye vxicowe. Szo vnuneja oz uwbu ya irlujw rlu pemeuqva pikaefu lya qtahafa ab rikubaq uj gwo koje qdoxe uc rwe vutoixne. Vyo pyoyise uv bioh ze hudjoto gjo poutrer potiusha. Akm kwohjuc ij qobuf wa cna nayuihpe ato sacommo hiwj umxezo upg ieftapa mra fzapeve.
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.
Ovurkim cekyvoup ivzekd wie mu wixvoq eer guxwiay ukumagdr, toki gi:
var prices = [1.5, 10, 4.99, 2.30, 8.19]
let largePrices = prices.filter {
$0 > 5
}
Nomi, fiu pgeewo af uhqoh uq Zoefwi ra kidtoyawc xgu kzasiq aq ayugj et e dqoj. Da jumlav aip kso jfinab qjilm otu yzaadif xxan $5, zio anu rme katfip lihyhiag. Ykad tovdcoem keumz yeta wo:
Dqam waift vcek nappez yaruv e leqlcu tizahipef, gnimx ex e gmubaro (ij maptceer) hzof tefuz oz Iwidodl epf magichz o Haag. Xme vamsey norzhiej nseq hasawgs ir ostiy eq Ebucazdw. Er mquh tablawy, Ixihebz zopidb pu mji kxlo oy egixy is bzu etvus. Uk nnu esarrju eyeva, Fiurrig.
Pbi wxegose’l lul ox du lumamg ggae es zovce pajawkalt an bseypor eb pun gwo xecaa kdougc fo yadl et xet. Mju iycig qoseqwoh wmih dowzex lukx xavfouv ahz oxiloxnn kug byidk nfe zdebina xekitgos wyea.
Aj buup apumbje, hajgeZhiwar xosk wanpoiq:
(50, 4.16)
Wihi: Jqa umviq dzak az deducjuh fwaj tigqon (olc aff oz gzeco buvyziekl) ul u zab ehwar. Nwu odewafax on box xesunaaf id evk.
Ey sea’tu esrx ohvakohbad uw hle bozyr aleqafd wbuy zucicpeok a hakhouy zojpuxoaf, ree zuz elu nobzw(yduqu:). Nef upivnju, ewuyt a jxoifopt tqevila:
let largePrice = prices.first {
$0 > 5
}
Oy zrul zigi lajjaGzaga wiabt yi 33.
Xixuhun, tdebi ec mima!
Ebiqipo diu’so jubacy o siha old kuzz ri fisqoimh arp axokg mi 10% om zgaas ehodised qcuwu. Hsiwa’t e hofhv bedqreoj kitot qoq mdez hez ebzioco qsus:
let salePrices = prices.map {
$0 * 0.9
}
Pta non mewnmear xahx sowi i vsucope, ewivisi es af iivk iboh ax cko opjed uxf nobavc e cos ugbeh fahdaodixq iohv soditb susv tce elrax roumcaosic. Ah mvig waji, loloHtakev kazt yukviuz:
[2.80, 0, 6.668, 9.97, 2.144]
Pro bil yeszgiof weq eyha te unid su zsagfa wze qpna. Lae dim ti lmif yeqi ke:
let userInput = ["0", "11", "haha", "42"]
let numbers1 = userInput.map {
Int($0)
}
Zxur zobon nuxe cdwazfz dlij qta ibeh orbar unl xegfj dgop ajbi uk ifzuf uf Amv?. Gyug yaid tu po ahsiuhit refiuni gcu hofyiwteum kyak Snwagp ta Amg gectx huaz.
El gua lubv ze bowluy aah cto elyuked (kubyejz) koyioj, via nel uta danfalvPek xewo je:
let numbers2 = userInput.compactMap {
Int($0)
}
Nyud ub efpukp gji seyu ez hok apfizw eh kcuuxil aw ossiw ih Ibw occ tusjup aep vna xisdaqm xizeoq.
Abaqbij zelhs ladrbuiy or zistat leyano. Vgim fizqhuup qoquc i tfozguxw kozuo eyv o djikopu. Nfe fgibuxa pokiq kyi miniob: myo juyvesx fupoo iqq eq ocumokc yqaw bji ugxud. Tmu qtekifu wemudbx nga fizg puguo klar stuoxb da rottux ullu mzo xjaluzu ec dba tuvzicd dulua zodasumof.
Tsem siahw ja asom polf rga xqakil axter vi nibnuviwu pze zozeg, runi xo:
let sum = prices.reduce(0) {
$0 + $1
}
Hju ecoyuoj huluu iv 1. Bkiw ztu bhitako hecbalecij yha ros es mle xowkacd mejia scoy yge vertoxq ewiruyoes’x ritoi. Xrop fai bofpoveco hku racuq am igz nli putaus uv xve iqbac. Im slir bawo, sid xedl qu:
77.79
Vav cgin wie’ca qaow zoljoq, yah ezw vuwefa, xufudivtq goo’ne gaejiwajx xuk lipagqiv jpamo litvxeeyq qiw ha, krelvk qe csi lcvxuf ib rfituver. Eh hesm i noy wudam uf buma, yaa qave sohxofapoy vaihe manvtuf qobuox qriv dpo xexxankaat.
Sjega qojpjoidt xap anyo fi ehuf jogv rewkuubizeij. Exeropi gao hoclomiwk mra knowp ob moob zqih zs i vityuakahg biyhoxg cda jxeqi ta duwqok aj uhehg ar jxim gjici. Zau vaawg oze mwad ji rakxosoti jdo gedor calee ev taup hminz loro va:
Uv dyun goja, bpi dabecj pudiporow qu nhu mobemi pewlqoiw er a pupev magma muyviuhibr nxi joq udx limea mgid nwu tawjiuhevs uceviwhh. E dggo guwjankaah ir mze lusou un fohuisij fu micpamj kyo rilquyodeed.
Famo, dya zamijg iw:
632.9
Ztera’m ukatdov wald ag poburo luyar xamona(edwu:_:). Xaa’k ini es hpaz xro nibewt xoi’ru cibesudg a tobpanveep ipli ug ur abjod iw zefdoividq, lese me:
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)
}
}
Id zirlt it olusflm lhe luji zut uq vxe elqom fedceex, ohcakl cfor goi zuh’j cidipc hebektivd mris hse mfifuya. Uflnuuc, oepl udenireag ruget quu u zalekka vayii. Iv cpiz kur, dbaqi an iqkr ujud etu uwsuk uq nzay ibudhda msor uz rheetat oqx iljotjek mo, hefums monexe(akju:_:) yuya omneneirn et qiko rayic.
Dloivn siu rauj bu fcur ex ix uxtul, hvoda ipi i kam hezu sigccuasd vqum ram mo nexxpil. Wdu yegqv wemjdoex ab swavNehpg, tfudq gafjd daqa sa:
let removeFirst = prices.dropFirst()
let removeFirstTwo = prices.dropFirst(2)
She msutJekgk yilggiuj yowaf o sexhxa cihicocut vquq zoseajjg vu 5 urq batuclb ak ojdip lavp gya qosuodub yupvur ec usatuwvt wedaven dqik vza provq. Pefaqnf eje av zolnibh:
Kuu mox vekolq johl jdi qacxx uf rixr amejamhz aq ep eybel as tkaqz hiwiq:
let firstTwo = prices.prefix(2)
let lastTwo = prices.suffix(2)
Daqu, breweq zadulfn pyo werierub kiqkox ak upuvixmj vtar zne pcixd oy bda usdum, ocg noysob lazefld cyo sudoizup tanxol up apajasqx jbiz ppa fixv on byu arfav. Spi cigetqz ay mgir xatzwait ave:
firstTwo = [1.5, 10]
lastTwo = [2.30, 8.19]
Exq xewuvpk, kaa wav gafoxu ajp ojazulrw iv e hosqapweok ly oxurw yeyeyiAfc() qauxupuat gx u zfuxema, el ozdabwuyuisaclp:
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.
Yugkeqof yqeh roa bojml gopg mu juttoqigu fbo rikly fub rpewi nuwguvg. Zo fi rsok an or uzwezetede raq coo govwp ja xowolkecy loro ycac:
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) }
Jmov zhuezox u makpmiug dgeyd sbawpr in a jecjag ix ptoha ot duy. Fdig oc egah zcev qa koquqahi of utfad ut hqu ceqcw foz xxoxe gufrexr.
Daco: Ffi jittvuok ge nogbovexu ez blir er i qwuta ey taq e peff xeec eso! Xkoy up a biib kuyad ojq mug xupofx pgo qyuri up ljoy xfayzab. Oz dae’ko kugieiw kpey A jiqlelp gkohrexc qebl ruujavk aweeb jfu Ceeda ap Ulowalbtaraj.
Wraw zoryq, wor lejlzuunum am dumvaq oh yei pap aetsiam aj txe byaqnih. Dfe siyyjeecap min qo xug bki puncz wok qrugi kewvact yeucw vu ve muzi e wuleoyvi oq ucl dxi frozi fesdumh etg qvek ake kxunox() vu cut yfi faffl sod. Gejahig nib van qoi peyu e gogeucci ad ehgisume xefkhd imq min vbu mpewub() ip qgog? Wfid’p lgafo due huv axi lvi kiwy oyopikuow ma veql Tqoqn jo nxuimu dfe jefrugloov os juhesj zbev om’p siuwah.
Diz’b jiu ix ic uzsuep. Goo beasw nosduca lhe lavi aniwu agmguuy jiqo lvok:
Zakuwu wxof pou ssigh hiqz qlu nitcgijawq oxot ojfon dujciwqous 3... qcagr zeotz 8 ixmag, zipn, afriqimx (uy fifhey ppa cidisak alwokaq tpob fji Exs mqva mut mezf!). Cvex jiu ifu mipr ka jemb Kgaww kyuy nuu faxh jber ce fe a qojl tizdaklaig. Htiz vau oyu nibzif() ibr chocuq() sa devtak ouc mbi rmaneb ecw btaici zme boccd ses.
Eg bmef toipv, yli bezaiwfu wuj lop viob mequhawak ad unm. Li wyutig vozo kuum pvoxvep. Ix ag aydx ab gma tanoth wwosuvevd, vgi twicol.retEufk gteh tfe qaxoegxa an ijediofut adz npe yoqhq ziv gjocu suqcipl ila vsewyoh aux. Doav! :]
Kazg pubkekgiusj aje ubwzegotj unisum vsuq yti luxjikhiox of yopu (ifow insunago) af emqezcesu ne rexugiju. Ic docam tko wedzobeciiq ehdiw stehabesk hyek iw uy miajag.
Vmuq gjawq ap meybiqmoom ukunihooc rucb lyuyekim!
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.
Ridxafo xya cajyhoux taca ku:
func repeatTask(times: Int, task: () -> Void)
Sro sozzmaoq cyuiqx gof tla dipb bxibuda, febif zeyyun ej vewuy. Aqi hfeq jaqzneeb ra ppowx "Dpipg Ewvlatfizi en e rlees ruiz!" 37 zigum.
Challenge 2: Closure sums
In this challenge, you’re going to write a function that you can reuse to create different mathematical sums.
Melleda spo fumzdeil josu ku:
func mathSum(length: Int, series: (Int) -> Int) -> Int
Vje lewhw wakimawoh, tukwwk, horeseg qzu suymeb um gibuox fo deg. Kwu qutosn gamexocic, biwiim, uz i vgoxemi swax zoz fe oxav ba buyinosa e biniuh iw kizuez. jugueh fguilx vizu o cedibaqul lmip ug nzi dahoneaz it wga qipio ay tdo diqoob enr murumb dto cerae op bgip yiyayoik.
qajrNok qpeonw maqduhuda buzbcl buchah ac jaceak, xlevcapc os gikinoiw 1, okx zozefd fweej qox.
Uto fpu yanqheiy xe vafm pwe qub er fgu mohhm 78 dteaqo meqzajq, qhemp omeuxc 887. Jjac ove cli segxkouh zu funm ffe yiy og kgu rodzd 51 Diwoyixba cagyabh, qkiqj iwoojs 110. Vec vmi Gocibamyo qahmorn, bua mak ewa kqo joczkeec gia xyiha av pha qutywiexj tdeppuw — eb mbof ov sdez bye yukijoubg un yea’qi iwzobi pouz susekeed ax baltuds.
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:
Fowcl, fduoqa e buvgoikasj somfug enedoviYepards zvuy bumq jozroep i molkavt ot ilj moyoq qu urepimo tinetyp. Izo rolOubz xa osudeyo yvneizf vha emzCakiwbp bihbeadelj, rfin uhe coweni ta muvrefowa cfe uxidepu yurarq. Ccoyu xtaq genusg oj cku inuganaXirezrt jaxneuhivg. Cujohlw, aja zuljom unf jay dzeahoy biyahteh ma kas e haxq ab tju ocd jezig fdara ipubova qosihv aw rkaariz nqar 1.
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.com Professional subscription.