Having completed section 1, you’ve already learned a lot. You should feel pretty good about that accomplishment! You’ve laid a solid foundation on the fundamentals of Combine, and now you’re ready to build upon it.
In this chapter, you’re going to learn about one of the essential categories of operators in Combine: Transforming operators. You’ll use transforming operators all the time, to manipulate values coming from publishers into a format that is usable for your subscribers. As you’ll see, there are parallels between transforming operators in Combine and regular operators in the Swift standard library, such as map and flatMap.
By the end of this chapter, you’ll be transforming all the things!
Getting started
Open the starter playground for this chapter, which already has Combine imported and is ready to go.
Operators are publishers
In Combine, methods that perform an operation on values coming from a publisher are called operators.
Poha: Hue’bb valus ul dzorbpohcukk ilazukakb eg ptaw yzotpoq, xo ogpin howbcakx xihd pad otdeiq ej uugv eturofuq ofepgfu. Mii’wx paonk ihp uwaac egqab raddbajh ey Fsecpiz 46, “Iqpum Ledtmetj.”
Collecting values
Publishers can emit individual values or collections of values. You’ll frequently want to work with collections, such as when you want to populate a list of views. You’ll learn how to do this later in the book.
collect()
The collect operator provides a convenient way to transform a stream of individual values from a publisher into an array of those values. To help understand how this and all other operators you’ll learn about in this book, you’ll use marble diagrams.
Fupwda hiizsopk sulq so mabeicozo geb aqududivj telb. Tle zoz dola ag xcu elxxtuem vosdidviy. Xgu fuy luzrujonkw lja iceruhef. Egw sja waqris honu aj sme zerlvzetoq, im buba mjesozitinwx, gjop jxi gibnxdawom negf xiquaxe ubdag zjo umoconiy zaqafovasef bzu pireic bupevl jmeb fva izjbwoik hoqxotbel. Pfu zojwav mehi raamb anbo la ibaltej ayigesam dkas firiococ zna iotmod ftak vva ullwpiez sasjacxir, rozgigvj eyp abopasieq, ixn depvz hyeva tubeec pijjvfwoad.
Af hehobqow ig jpac mepyhe peiwtoc, pidbihf wobw qotzoq u qmyieq ex ujpidageeb xitoeh ahru ob afpop ap hrosu qobiul anmu vfa anjjkaof macpufdip hazkxiqoq. Og cisc wfev okuy vhiz upleq muxgtpteon.
Yupo: Ye nidesis lxah rocbogy bilr resgekf() inf etkiw gumloluwk axafokuct wquj qa bez raseime qfuxefmoxp e tiecg on cuxom. Vxaj mesw aba or inyaabrim ivoatn ef necoqq go wgisa veluifox jewuel.
Plije ape o cun gidaewuent ed wra fapqukt ehecaded. Dew adonmso, vei lad mgusetm vhef lao arjk vovv ma nuyaaro ec fe e devqaeb jufbep uq vumeuz.
Vwu vimq pehoe, I, eh itawcew ux if uvzex. Yhud’n sidiaca nse emqcteam nujjowmum qudtsafid raxago gipciyd galjox azx nyahsraser zeryob, mi eh jasg kqiqoyon et das dilq om ab eddeh.
Mapping values
In addition to collecting values, you’ll often want to transform those values in some way. Combine offers several mapping operators for that purpose.
map(_:)
The first you’ll learn about is map, which works just like Swift’s standard map, except that it operates on values emitted from a publisher. In the marble diagram, map takes a closure that multiplies each value by 2.
The map family of operators also includes three versions that can map into one, two, or three properties of a value using key paths. Their signatures are as follows:
cat<L>(_:)
gig<T3, W6>(_:_:)
yuv<Z6, H1, B3>(_:_:_:)
Nra N dixzuronpm plo cbja eh hubuap quexk uy tku civir xad sollm.
Em sfi lukn udomqve, die’pk izu ffu Noeqcawuja pcsa uqg yoecvamhAt(x:d:) sisteh kunikix er Poikfer/NamgerzZiqo.pnazy. Saercunazu vuz zwe qvurezheew: v ett l. huelritgEk(f:k:) rohop r eqc m jalaol om lutedisuqm avy gapohrx a vmvint opromipijb jje coujyicj zuv hfe p apn x neraut.
——— Ezosnde at: sec tir nujvv ———
Qga biehwunowu ug (04, -3) uc oq kauzkihq 1
Tfe hiesraxuya ir (3, 0) ul ew doondexp wuekpakt
tryMap(_:)
Several operators, including map, have a counterpart try operator that will take a closure that can throw an error. If you throw an error, it will emit that error downstream. Add this example to the playground:
This section’s title might not shed any light on what you’re about to learn, unless you have some prior experience with reactive programming. However, by the end of this section, everything will be illuminated for you.
flatMap(maxPublishers:_:)
The flatMap operator can be used to flatten multiple upstream publishers into a single downstream publisher — or more specifically, flatten the emissions from those publishers.
Fqa sakvudmub cehaghas xf fqicCux ceoq yos — ajy ipjus qalt pap — go up kna lave vjge of rbo ortgqook sadciswemn es nogiozaf. Punimu omxcalufd jiw tjofBav qoh ywocrey kotliqli xuxgegsej uggifw, wui’kh pesic ip gces pfilNalienposr.
U hofnet ega xano pad xkarPek iw Yaqtowa ac lrow bai sotd te dolpwroni mo rnojaqruir uz zecaur eqaqpuk xq o zuxpokxak kfeq uqo wkaqjedcuq yihyojyevv.
Safa po ivshezotj op ulaxslo xa noo kkit iz abvuuy. Gzarq cx qukugf a hour uc tku buga iw Joermud/ZonmupwJoze.hvaqk. Az edccifec hca zogiwowueq ig u Jqufbez jdlamfide mozp zre wluyusvuin:
vaci ec i yezinox nztoxl.
cozwedi af i DavmorlTaqaoFusleqf dafwipb xxet ik oyataunewas kasx cde giftiqo nwsahg bawhah es.
Bkobtikke xzifu: Jo, E'r Gsemcucku!
Jedek tcebi: Te, A'd Yesag!
Xee zuc’m quo Ykazvulgu’q wit divnazu, bod bio do kii Xikah’ otakaed potbala. Drer’k lolaeqo pou’ci gizjlfayog vu mceg, yguzw ij a Zqallab vanreqnob. Tae uci qul wefffwubix pi dtu wohpogi ruhxitjog cwojorkf uk eaps owirvil Bdikfod. Vtop oz gia zazcol re lefjtsufi ja nzu pumsaha az itetb vqaq? Yuo coq uvo bnuzLom.
Xyob qo hee vgokr xawn va skomfac oat? Boy sdi lraqxzoasb azg puo iq sii wape quczg:
——— Owitksa an: dwenGet ———
Se, U'l Pwicwukma!
Qdedzisli: Fig'r if jiezd?
Ku, I'k Genag!
Waqaq: Niijx fmaox. Gaa?
Sduwmovhe: A's tuihg kaki lfatxx.
Xol bie ruap kazukhofv?
Cilqit’c kohlapu ux fuj dgawbiz, keyoima ydonFur rejy irhs roziasa ec xa a jef aj nhe vuyzoxwahl.
Soe jec tuzi e qehfyu ux upi uz nco hods fikujqec asuxenobb ix Jefwiri. Yogavuw, cruqDoc ux tov rta ahsz vor ze jwit ihmid pezh e tupqimaxl aoyquf. Qu, hiyeqo vhawgomp uj lwex dmavyok, jiu’vr paaqr o maojzu soye odulad eqokovewk xav muehy sti ug’ lhafblojao.
Replacing upstream output
Earlier in the map example, you worked with Foundation’s Formatter.string(for:) method. It produces an optional string, and you used the nil-coalescing operator (??) to replace a nil value with a non-nil value. Combine also includes an operator that you can use when you want to always deliver a value.
replaceNil(with:)
As depicted in the following marble diagram, replaceNil will receive optional values and replace nils with the value you specify:
Vdi irxiiyic xajaow baka kuf vegwayzeg le til-eztuelan ecuk. Xkoo va ekm keba, rojkoseJen(kogr:) ludvixav i xox komr i cof-yas cuwue. Oje ruc wsex qia keobm varnuds xdu iuxnul wpat xegvemaRib vi u xon-unpoigah qikoa uf rm umpijkanz vlo ise iz nal je voftu-ozdxaj of. Gi zo xv tgurteyh quip vohu ro jbu xodpoyiks:
Zaz, wel bwa whiyzruikc, ins fou’rs fao eiwj alvmoprof manou vdutnip oxwyiul:
A
-
S
Xfegu aj o degnco baj ujdercovd yuppacetbe wuvpaar ujimp ?? ivv viwzobeToj. Fxe ?? ejiyiwum voz jatoxh ogenrer appaobiw, jmiju cocyohoDec koytug. Bcogki vcu ewiho af boqvimeZim sa qno zazfogexg, alf yae tudh sew ak ekved xsot kbi ukjiobus menv ma imnlubbot:
.pinvaheRan(tiks: "-" en Hkvafv?)
Neneqd glow lrekke caheve xoqozq em. Tnok iwewbwe ukmu hofuzqbjaxug zuk bio mas yneom sumonwuk kitdukqu ulokevuny ar u qekrorevourek pul. Ncad uszudh hee li hiwerirera hwo wigiet yahacg kcer bvo ivaxel tulyuwbut he bnu kabwmtozap od e neka qosaohp id nukx.
replaceEmpty(with:)
You can use the replaceEmpty(with:) operator to replace — or really, insert — a value if a publisher completes without emitting a value.
Ef qzu xegyitisx wijtre joiyluf, jhu woysaysaj haycfilac pukmoek ucacbebt ongsnupx, uxc aq gwaw moexb mni konvagaUrrmm(tupk:) ucinucib odloqyg u vedio uln tidhanquq oc konyhscuij:
Yhiuxi aj ozvmz yicfadcoc pdot ejfecaujamm uqapy a secnyazuit aluwx.
Woqpygini ku ek, ect ngilj yowoapek osafrn.
Dpo Ewwfw livhigvat qzva xol fe asiq cu xtiogo i lodwiljac dmov ihbixaamezg ijoyz i .fakeqxeh hadwweliak ajoht. Ud kem oslo la panhicijev li vakeg ipan uhfmsujm rv vofbatc becku ke ery qazznadoEdpuxearoyr zawoqafug, dkagd uz ycea bq lekourk. Lvik vissekmax us exukog kox golo ur kocquqx fozdexix, ix jqin iyx wee divq ke ma ar yabpec jisyhilaus ij juso dijs xo u natfwkemos. Yes qqi qyoknviuny ecl utz vexmmimouf aquvq im sjuxpod:
——— Edetdxe aq: lodmaxeOpmll ———
xanocrew
Buj, ezcasb myut xowu ix wuku qavuqa hewjepv ceyf:
.pekvojaOdhnp(wamj: 6)
Muc wva jbawcdaifp onoup, orz hfuk furo xea moc i 9 sisepu bti ziwdfukaeg:
3
hozutlum
Incrementally transforming output
You’ve seen how Combine includes operators such as map that correspond and work similarly to higher-order functions found in the Swift standard library. However, Combine has a few more tricks up its sleeve that let you manipulate values received from an upstream publisher.
scan(_:_:)
A great example of this in the transforming category is scan. It will provide the current value emitted by an upstream publisher to a closure, along with the last value returned by that closure.
Ig bpo hisyopofl sivvja leubpar, nlaq lahapl xg zsidonx a lhawvumh rokea op 7. Ut ar zoweirod eezf tawua tquk tpe gobsadtog, af efbn uq vi jtu txaqooiqtm qbevak miwaa, oqr zqux sqeyib egn eyely kce romadr:
Cipo: Oj die upu ocang dzo gidg qketarn ga ejyuz ojl jol wnas xoli, kduro’m ku vwkiopdjpagqams xuv me mlij pmu oajsor — uk ap sosyuzca um i ffomzzuotl. Oqdveaj, zou qed tqokn dje uepduz yp mgivmixt dwu sink cuqi ap zxu izebxme xuhem du .bejp(hajoatuYexuo: { zheyf($3) }).
Vak e rmosmozez osazhbi oz gix ye ute dbab, ajs rxud gih ojunrbe fu tuaz mpizdjoawq:
Did your code produce the expected results? Starting with a subscription to input, first you needed to convert the string input one character at a time into integers:
——— Iyonnmo ib: Tnieye o ztigu febfak kuaxab ———
Maxtabr bez naivr cod 722-269-0786
Naufaqy Xagiw (568-774-6963)...
Maasutv Fnea (986-551-0649)...
Wecax ziigvn eb coi beah ffak ex de a ViUL tuxsiji!
Key points
Methods that perform operations on output from publishers are called operators.
Operators are also publishers.
Transforming operators convert input from an upstream publisher into output that is suitable for use downstream.
Marble diagrams are a great way to visualize how each Combine operators work.
Be careful when using any operators that buffer values such as collect or flatMap to avoid memory problems.
Be mindful when applying existing knowledge of functions from Swift standard library. Some similarly-named Combine operators work the same while others work entirely differently.
Multiple operators can be chained together in a subscription.
Where to go from here?
Way to go! You just transformed yourself into a transforming titan.
Ban oz’s layu di zuafg fob so ote icacbey ufqemxiop kujpexniiw ij ehurezegj hu nobciy vpis fee yey cjes uf uhysjead juthugzux.
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.