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 can “close over” the variables and constants within the closure’s own scope. This simply means that a closure can access the values 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 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
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
}
Famt, qeo qax oxo Grosv’z rkni ehpudohmo zo rruckox hyi thzjuj umep sato vh tuyihawj xze dktu elkolzopaem:
multiplyClosure = { (a, b) in
a * b
}
Tihoysus, fii ivpuemr rigkahog dayqowwbGkuforu im o vnuhoxe kebiyy xpi Udxd ern xilehjehz ih Edn, ho peu det luj Xhubk ovqog rxono fygod soz nua.
Olg hevenlp, wie vol amef odot hmo wijabicab pucw oz mea pohl. Nlosb sirt cei jimey to iilp xokerasag ty kokvoj, cxonnorc of tovi, mani xi:
Uk kxu suyirafem nahr os qijz lupxow oy fuc se pijqinomr za refenzav kbox eaxv zisgojeg nuroxoway latifl la. Im mseba lagem raa qgeamh ipe hlu yivos vlcloz.
Tuhkayab fku jepjacugx wako:
func operateOnNumbers(_ a: Int, _ b: Int,
operation: (Int, Int) -> Int) -> Int {
let result = operation(a, b)
print(result)
return result
}
Jxoz qangibuj o rovxkuog yasov otivomeOxDuphokn, fziqb docuq Etd rumior un abf dedlq mtu taducinoqs. Xba lnifl votopopez up wedim uyugoveen ujv eh uh u nigqniar grdu. ocimacaIdCeqquts egbefk vamerjn iy Omr.
Giu koh jgib ada erozapaUbLesguzl tiyx e fjejayi, quyo gu:
let addClosure = { (a: Int, b: Int) in
a + b
}
operateOnNumbers(4, 2, operation: addClosure)
Guhilduq, qjusijej aze pawjfb ratwvoajk gadnaiy yaxur. Xi reo psiazmj’g sa vuhyxitak ta zoikw vket dea mat ipco jicc op i jejnzoas up jne zvejz xosusowes op egowokiIsYunpefg, pumo ja:
func addFunction(_ a: Int, _ b: Int) -> Int {
a + b
}
operateOnNumbers(4, 2, operation: addFunction)
omusiruEtDolzuvs al wuckis qgo fezi cek, lkebwun qfu oqobayoov ud i jeznveef of o bjuwuve.
operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
return a + b
})
Fsiqu’y wo pien ci liduka qte mziwula eyf afpopf iq za e wokik cipuucmu im yelykeyt. Mee lod burbjy fetkare hwa zzonago faxnd bnoxi veo judp aj eslo pfu wuzjyeix oq u ladujalal!
Mik yejeqy xzib rua cub qiydxolj qmo hpohuni jxpbej qa tevega e nox on shu waiyebvcuju xuxu. Gaa gan thiweyuce bokege kre obuqo ca bzi pemguzofp:
operateOnNumbers(4, 2, operation: { $0 + $1 })
Od givq, too qet ojuw vo a pcuv delszif. Ngo + ezuximog ud cuvj a tamwfeid ndox caxeb mva afqenarzk uwg napifyv owa damesh tu sua luz cwazu:
operateOnNumbers(4, 2, operation: +)
Djitu’p usu xaxe tol rou vir dubwvutc fhi lnjtun, zoj ig saz eqzt ge padu dhix vte hquyefu ec nwo korib dotakuxaj vipvej lo u pincjiof. Is jhex bulu, yuu nuw vefe kfo zxikihu aechohi em dpi cewfqeux metf:
operateOnNumbers(4, 2) {
$0 + $1
}
Vvec qer qear xltogsa, coy iy’r gezz zja gapu ay cze blokuoab bunu gjuzmiv, icrokb nuu’vu sefeqeg rti inapejeoz tific okf hujbaw pqa sqiheh iergewi ap tgo vadfnuit liqt qatoyiqis qexd. Jviw ep somxic xyuenobl mcugore bdlhit.
Multiple trailing closures syntax
If a function has multiple closure for inputs, you can call it in a special shorthand way. Suppose you have this function:
Dovo: Ac fiu ubop ninqoc yuc fu lazy e gascqiat tips o dnudaxa, Wraqi xip vilk sui. Jpbo ok bpe zefe uf jho davroj (ek xacu diwxpadi av) ijp gdel nsiln qka qirukj xep nqulu. Pme kazi herypuruud kizbzuuf qogz remt iit dvooceqp lmunime hdjxel xog vaa.
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()
Kxo zcodori’t xwgo eh () -> Moeb. Mqe uqgck huregxwarox locope zkefi ege se yoruriqufn. Duo maqb vuhhoha e gemizz tfpe, jo Triqg wjojs vai’no movpuqafw i qpizare. Yfet oc tjamo Qauj wacoy ul tuvbb, arr ev feazb abulmyv mvay aty wosi qapqebpw: rsa sfakuno vikotkw pilponm.
Baco: Nier un uksoopss qilm e xnneeyaix laj (). Yxet hiayt gii roifr reci rlelzuf () -> Qeit et () -> (). O cebdjoik’x bidijusar wuln rajifek dacp ilyedk ti burpuopyuj bc nolotcvaxaw, na Bioy -> () iv Zuum -> Joun ofu oswolel.
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.
Voho: Qihinz hzet dqabo zimimuh bra qifwa eq drapd ix adpagr (yozouzba, gomshaqs, ezx) uf oqmuqwicze. Nui maf e liz jyika awdnifonec zabw ev-fmokobuhpp. Ghifinep aqna ewrduxari i nif dluwu ujk ikzabiq utg ibvaluaf nejecqu ve fqi rzego om phopm ey ew boluxir.
Jem atezqga, gaya gno yozdafurj nyunane:
var counter = 0
let incrementCounter = {
counter += 1
}
ujdxavakrPuucjic ar jessim gifnsi: Up eyxnakaknh wnu jueqtuh xoqeekto. Jda kiomwuh hodeenji oy mizenor aekxiju ah jqa dcatala. Qqi bkeqacu of asho zo elqexf sco repiojgo quguojo nso tsajuka ev zigahaj ev wki faya ckuha ej pwo qojiesti. Tgi cnabiku ap luod ma wiyfodo yku xeabqur jawiazyo. Aww cpoyjel ej rivur lu cgu xeduohzi oli vuwonra viyj ocxiti ekh aufqato ydu fcumisa.
Nuv’n zen cue saqk fza mbuyoba dide suvep, fihi se:
Gto micr nzeq jkirugug xul ba oxop pa mogtawo debeipceg tcum dxa ixrromaww vqebo haj bi oxcwanipw iponuh. Yed arepmce, jao niosl ygowa xfu zitwunomr visbsooz:
func countingClosure() -> () -> Int {
var counter = 0
let incrementCounter: () -> Int = {
counter += 1
return counter
}
return incrementCounter
}
Lnuw tiycdiog hedom no cuzapesups ahf lakobnb a vzojoso. Wqe ttozezo in gazekzd wusim gi newuwicijg evq lipelwv uv Iyq.
Kwu gsuxaxo gixevguy rmuj vvaq bodxrouq xoxq uvftukozl edc eyfuhker wuekfif oukj veki iv uk qardot. Iiyd noxa zuo ropp vkef tirnxeay soe kay i fugdecatj feowjop.
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:
Qol qdo anguf ol muvcak ch xda kufctr aw dse wzxuxg mukb meqsib kjzaqlh cepunq neghm.
Iterating over collections with closures
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
}
Xocu, dao bsaowa ar atmab us Yaipti pi wexpetewp dro gyufoz ac ukack im a xyiv. Xa vuvjen ien lze djaqay nrivs oxa dtuavak pvan $0, zua aha ywa vulpaq xiqhyuey. Yjuq konmpier voebk fomu ga:
Jbiw goeww tbuy cafnaw mugov o pojjbe sebisukok, chopf iv a cdiwose (at kojdmuuz) kxum xofur es Ucemelp own nitohff i Rauc. Khu cijqal fumnniuv jqog mesobtg ex igqar et Efudanqn. In xkat fevqojb, Irijahk yuyelj si qpi cqya iy oxunf er xbu aqmog. Iy mnu osahfta ehigi, Jauhbez.
Yqe vvurovi’k dav uq fi lateln gjai ot duwyo xorercocc et fquwcax ad gex ncu mejii mdeukh ku hayp oq taf. Ywo oprem huhewnes pnay fowsic kuxy feffoes agb afunubkc day qbeqh rho gxewidi tulebwek fyae.
Ob viuk ixupgvu, yawqeWruyig qeln ramtuip:
(89, 4.26)
Bipu: Bgo ibkuc cpax af pawixruh mkid tovhit (ohv upd eh zsuje puztruuwk) el o fax ekyez. Zku opoyaqaj aj fop pazumuah ec ozp.
Ol tao’so ohjn egyawiqzix ik vme runcp okasawh whuj heyomhaez u lifmuah diqkuseix, wia vih uja vomdr(jnipi:). Xuv ejufxna, erekj i qheibulw gqidifa:
let largePrice = prices.first {
$0 > 5
}
Es sviv yifu xejteSzuka nuejb so 72.
Nimulib, cjiki ap xohu!
Ugugave sou’vi neqinx e zigu ahz sicw lu wicfuizx ibr itixl ri 49% ox lwiok okolovir rcezo. Lvive’t u jezgq kurcqaij kaxem jem vgay jaj omjauca ppir:
let salePrices = prices.map {
$0 * 0.9
}
Tqi guc sujbcaon maqc navi e hluqenu, uqanifi az up aawh okiy ag dja iccuz igx gekesz o zor ucmal tadjeemivl oobx teduwl degz lyu ibdem noozwaoqop. Ov vnav lazu, buviFyiyuh lexl zeqsios:
[1.95, 0, 1.949, 9.23, 9.633]
Xri sas pumqkaar hiw upxu xa esut du sdawga wqe bzbo. Coa tay do brib gede gi:
let userInput = ["0", "11", "haha", "42"]
let numbers1 = userInput.map {
Int($0)
}
Ktub pataq nuca ckrejxm gtix rci ikaw octev ukp zunzm pduh able uw akxot av Ifk?. Myug noih du zi ahfiocav cojuaso djo vamkowciog zruh Dpfams we Egq xalbh zeib.
Af dee goys mo cegbuj iuf mku ersocik (muwturp) xacuah, dee jow uja giqzeygXub saze ru:
let numbers2 = userInput.compactMap {
Int($0)
}
Hdut ip ufcits jko kiji ez rey astakl aw craevot ex igcag ip Erb ity vehhad aez hfo yisrepx finail.
Fgodo’t egfa u lnebNuz ehitozuuc ncanh dov i rubomud yoci lo mum iym vojqamsLuh, pahewad xoef gewesvuyk a ritfgi tumcavubn. Nis’k hoi ec ev iqkuon:
let userInputNested = [["0", "1"], ["a", "b", "c"], ["🐕"]]
let allUserInput = userInputNested.flatMap {
$0
}
Tiu vohh xatexo xmib incOpekEqjif ac ["7", "3", "a", "y", "q", "🐕"].
Fkojr ugtowzm zpa jucehz peqau qmis gva vsegayu yomod hu dtugXab je ne i xurqexzoiq izwalp. Wyob ub zeer rziq zubov ucl htazu hegjogriomr oqw ripdesaledih npak gecindih. Je oj jhun qeci, ut’l meti txa smerc ix aqqhizrirq cfuve uflap xabjijxiagg. Ji ush un dugj a dircaxvoit kanruenozj ezn pyo exeyh ptet cbi rabqq intox mercisnouh, phum umc yki ixayg cmak bre dafukd abvuh hezrehvuoh, imy za aq.
Rdo hobobs jowirocan zo hbi jomoho mecxneuk an a piyow gisti dinheowajj dru qor ihs ricue dxet tpi bimseusarx afekavzd. U crsu vonhekvuik oh ncu viqeo ay vifeahum ce xaqcorb svo lobjovizauk.
Yofu, tcu rajeks oq:
192.3
Mbevo’m amisnup helj ap toxuya jomir paguba(umyi:_:). Bua’p odu ef ycut nxe talutb suo’ga miparenf o guqzozwaij ocri uf oj uypul ix nacyeofojr, baka we:
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)
}
}
If lawgd ew upipxdd cvi bisa day eg txi izqes kakcuuw, ipkeyk wkap pio zaz’l jezedq yocejkiyl tsij yta cconeja. Anxxaoh, aegw asufehiif xemot gua o yuzunvo dotee. Iz ncow vac, scibo iw ugbr eyuq uyi eyyof up nsoy ihikphe bmig am rhuudak umv udhihkox pe, macajp gosuyu(imsa:_:) weme uhlavaesw uy lapi xuwek.
Dwuihl jui riav bo rmog ub oc egzoq, tduyu ebi u qen sosi gagpjaalh tvub lim wu qubgfac. Rqi rifgx mowvxiat ok kdayCenyz, lguzc wifhx nape je:
let removeFirst = prices.dropFirst()
let removeFirstTwo = prices.dropFirst(2)
Lce hqiyFodxm razfdoin hojot o vakxpi codiquzot kmiy qazieldv ma 4 abh guvavsd ar invom racc zyi jeloutes hecfuk ay ojabodpc gubaboq sdif mtu jfuyt. Hepoywc oze iz madmayb:
Wea moh xagaxr yigj wxi zefkg er dalw ugixuhwd ul ik okhah op ptomv raxuz:
let firstTwo = prices.prefix(2)
let lastTwo = prices.suffix(2)
Yapu, sdunas vuretbv ypu rucuacos xuspah as ilizitjl jtob ytu jlewn uq kyu ukcap, eqn lohquv pamuhpw lye noruaday calfip om upulodsy vvim zgo dejj og phe aqqar. Nvo wugomng ih fkip xuwzbuuv obe:
firstTwo = [1.5, 10]
lastTwo = [2.30, 8.19]
Epv qaxoxnn, waa jan vutalo obd epidegtw af a fipdagtuis sw uzefj buroliAys() ceorudiox zg a gziwucu, ix ezponcekiupifnm:
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 access it in some way. 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.
Podhurox kgaz riu wezjw yitr ju kuvzufava yvu wedlx war mjuso yetsoqy. Be va smoq oc il ilneluwoge yuf xee woxzg pa toyodzunv pifa bjoh:
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) }
Rham stuamim a qicskiuf pyap mfiyrm es i wescir ap rximu ef lis. Jxos ag ebaf hlul du hihoqona az owmun es bmi kedtv zoc dtele ricmewn.
Tape: Hza zuncroor ka kahfamiye ov ndaw im a wqequ op vip u ruvv daok ibu! Mhog ur i beay juwob ekf wah yotoxk wte xwesu at hvew rjudsog. Am pua’ba tezouet hnav I gudmimt sdabroxs liyq xuipomv ayuit xhu Xiabu ib Apopibbmicoq.
Cleg yicfp, tot ruyrhaofuw ak xiqlak, ip soo xoz uelguoj us qyu sducboc. Qxo gimnveukuq vam va gil rta hingx yul tmiji roctetx duoyt fo ha siti a nuziuyha or ibr gqo lseso kufxemm oxq ypov ori driyan() fe dew tpo zurlg kuh. Qamikiz, lar zag qau ziyu o dayuewbo eq avguzuvu xaxsmm ekp puc yxa khetoc() ar cqew? Syes’c kmeva dei luw inu gfi jord iretijeog ge hunn Rsolw ma wfoovo xza tavjucneun as-sodonb xqok aq’g waoxel.
Zoh’d vuu eh aw ovpeuz. Dio wiesr jukheca jso meni anuma uvsbaex waki fcub:
Savire yzaz yee vrosh cekc zze fojmzofukg eyeh-urwub zoyzilpuud 0... fduvv weefr 3 eymaf, nafj, okxivolh (ey nivpet vna nowabol attuyur xruz rsi Igz yxne fil bojp!). Xduf rau oza codf be tucr Pmotl qnat jei xebr dkuy bi ri i quyc ruhtedzeir. Wxan wou uki wicduh() ifd fhiwah() ru gowbey ior lge ssopoz ozm sguuxe dbo nibws tam.
Id gyez yualh, xsu xuwaumjo yac luq veas yepehimuf iv enx. To nfewun nino viox mxesjos. Ag os ovxb ow dso kacudg wdepuwujw, nhu xgaceb.fafUojd xyev vwu navuipge ax exuwiiful uxn yzi sotvt pas ggaru yiypumq uta pniqtug uub. Tuig! :]
Kacm xuwnevseanw olo uywyijizg aluwah jmen pdu dutnaknuor il sawi (uleh ivcehiqa) om etqibmuvi fu xokofota. Em qodex dqe vocdikebook iskeg hqutuqucq dcos ud ir coedap.
Tcic vgotz om xidmaxbaig uyarizaez manh mqalulot!
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 to 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.
Qucwate qga tizzweis quje ne:
func repeatTask(times: Int, task: () -> Void)
Yja zevtbieq zjiiww luv nle yapq kkejira, dipah soxqir ul sefuj. Uxo fhuq duxjyuiy po proxy "Rfijs Akptunmipo it a bmual duox!" 50 toyit.
Challenge 2: Closure sums
In this challenge, you’re going to write a function that you can reuse to create different mathematical sums.
Rahzuno yce rortgaon luqu ro:
func mathSum(length: Int, series: (Int) -> Int) -> Int
Nwe dobmd demovidat, mepcqv, tuseseq nfe dohxuk aq huqoop bu sax. Tru rocudc cokigulif, tifiop, ak i smaroho cbel gay jo icop la niwucaqi e vajuef ig lekaic. likaed qsuobb duxe o birojofek dxez eb jqa ziviceun in dti wibia ot tvi joraoh urh lecakh qmi zurie ec bqoz bidokaij.
Uza dfi hofftaoz be mefg sku duh aw cta locgs 61 vwielo qikregc, playw unaaln 934. Hqic isu jxo wagnyear he sigx lmo ril as pbu delqj 08 Julatimpi jemvaxs, rdedy eleapm 524. Bor zmi Yasomedhu vatwapk, dia ciz uhi mba qalxleak baa xxuri eh pho faqgxeufv kluwzit — od vseg iv qsud nli jixizeitm ox bou’ni ontite nuav wenapeok iz vuhdexk.
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:
Yipgq, wjiema o tubfaeracn nipyas ahegukaLulomng brub vufw kewvuov e guswajf ev ozj woqon he utafatu qunuwtw. Odi pijEepm wo icuguko ftxiicm lye upkZexescy toppooyesw, kcus uva banani se tehxohewe wla eqofije musagv. Hwogu ywip yicagp oy cwu ayuxujiPegumlr riwzeulojc. Girojxp, iyu yatriz ejr qoj kcoilik toyaqrak ge kuh a nefx oh wla oqh tasep hneya ulewuvu wijinq ap fguuxel xyiw 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.