In the past few chapters, you learned a lot about using publishers, subscribers and all kinds of different operators. You did that in the “safety” of a Swift playground. But now, it’s time to put those new skills to work and get your hands dirty with a real iOS app.
To wrap up this section, you’ll work on a project that includes real-life scenarios where you can apply your newly acquired Combine knowledge. The project is called Collage and it’s an iOS app which allows the user to create simple collages out of their photos, like this:
This project will take you through:
Using Combine publishers in your UIKit view controllers.
Handling user events with Combine.
Navigating between view controllers and exchanging data via publishers.
Using a variety of operators to create different subscriptions to implement your app’s logic.
Wrapping existing Cocoa APIs so you can conveniently use them in your Combine code.
All of the above is a lot of work, but it will get you some practical experience with Combine before you move on to learning about more operators, and is a nice break from theory-heavy chapters.
This chapter will guide you, in a tutorial style, through a variety of loosely connected tasks where you will use techniques based on the material you have covered so far in this book.
Additionally, you will get to use a few operators that will be introduced later on and that will hopefully keep you interested and turning the pages.
Without further ado — it’s time to get coding!
Getting started with “Collage”
To get started with Collage, open the starter project provided for this chapter and select Assets/Main.storyboard in the project navigator. The app’s structure is rather simple — there is a main view controller to create and preview collages and an additional view controller where users select photos to add to their current collage:
Note: In this chapter, you will work on integrating Combine data workflows and UIKit user controls and events. A deep knowledge of UIKit is not required to work through the guided experience in this chapter, but we will not cover any details of how the UIKit-relevant code works or the details of the UI code included in the starter project.
Currently, the project doesn’t implement any of the aforementioned logic. But, it does include some code you can leverage so you can focus only on Combine related code. Let’s start by fleshing out the user interaction that adds photos to the current collage.
Open MainViewController.swift and import the Combine framework at the top of the file:
import Combine
This will allow you to use Combine types in this file. To get started, add two new private properties to the MainViewController class:
private var subscriptions = Set<AnyCancellable>()
private let images = CurrentValueSubject<[UIImage], Never>([])
subscriptions is the collection where you will store any UI subscriptions tied to the lifecycle of the current view controller. When you bind your UI controls tying those subscriptions to the lifecycle of the current view controller is usually what you need. This way, in case the view controller is popped out of the navigation stack or dismissed otherwise, all UI subscriptions will be canceled right away.
Note: As mentioned in Chapter 1, “Hello, Combine!,” subscribers return a Cancellable token to allow manually canceling a subscription. AnyCancellable is a type-erased type to allow storing cancelables of different types in the same collection like in your code above.
You will use images to emit the user’s currently selected photos for the current collage. When you bind data to UI controls, it’s most often suitable to use a CurrentValueSubject instead of a PassthroughSubject. The former always guarantees that upon subscription at least one value will be sent and your UI will never have an undefined state. That is, it will never still be waiting for an initial value in a broken state.
Next, to get some images added to the collage and test your code, add in actionAdd():
let newImages = images.value + [UIImage(named: "IMG_1907.jpg")!]
images.send(newImages)
Whenever the user taps on the + button in the top-right corner of the screen, you will add the IMG_1907.jpg to the current images array value and send that value through the subject, so all subscribers receive it.
You can find IMG_1907.jpg in the project’s Asset Catalog — it’s a nice photo I took near Barcelona some years ago.
To also be able to clear the currently selected photos, move over to actionClear() and add there:
images.send([])
This line simply sends an empty array through the images subject, pushing it to all of its subscribers.
Lastly, add the code to bind the images subject to the image preview on-screen. Append at the end of viewDidLoad():
The play-by-play for this subscription is as follows:
You begin a subscription to the current collection of photos.
You use map to convert them to a single collage by calling into UIImage.collage(images:size:), a helper method defined in UIImage+Collage.swift.
You use the assign(to:on:) subscriber to bind the resulting collage image to imagePreview.image, which is the center screen image view.
Finally, you store the resulting subscription into subscriptions to tie its lifespan to the view controller if it’s not canceled earlier than the controller.
Time to test that new subscription! Build and run the app and click the + button few times. You should see a collage preview, featuring one more copy of the same photo each time your click +:
Thanks to the simplicity of binding via assign, you can get the photos collection, convert it to a collage and assign it to an image view in a single subscription!
In a typical scenario, however, you will need to update not one UI control but several. Creating separate subscriptions for each of the bindings sometimes might be overkill. So, let’s see how we can perform a number of updates as a single batch.
There is already a method included in MainViewController called updateUI(photos:), which makes various updates across the UI, disables the Save button when the current selection contains an odd number of photos, enables the Clear button whenever there is a collage in progress and more.
To call upateUI(photos:) each time the user adds a photo to the collage, you will use the handleEvents operator. This is, as previously mentioned, the operator to use whenever you’d like to perform side effects like updating some of the UI, logging or others.
Back in viewDidLoad(), insert this operator just before the line where you use map:
.handleEvents(receiveOutput: { [weak self] photos in
self?.updateUI(photos: photos)
})
Note: The handleEvents operator enables you to perform side effects when a publisher emits an event. You’ll learn a lot more about it in Chapter 10, “Debugging.”
This will feed the current selection to updateUI(photos:) just before they are converted into a single collage image inside the map operator.
As soon as you run the project again you will notice the two buttons below the preview are disabled, which is the correct initial state:
The buttons will keep changing state as you add more photos to the current collage. For example, when you select one or three photos the Save button will be disabled but Clear enabled like so:
Talking to other view controllers
You saw how easy it is to route your UI’s data through a subject and bind it to some controls on-screen. Now you’ll tackle another common task: Presenting a new view controller and getting some data back when the user is done using it.
Sqa nukoziz afuu ev evlsedleyl xeca yulqoen pba xeem bijpnixsaqc oy eloddrt xle xufi ac yiymkdejugh co e vommahb es wko tesi koiv juhwmuqcup. Aj rsa imx og spe xiv, gui bink seba e cigxuxvul yyiw axenn hido oebcev ovg woa ere a sokzfsujax ga wuse mipiqpijg avisic yutk xva iqewpuc cupiiw.
Cjtotg kozz ka uxroedEzl(), zumlalg jki ocubronl legh ap jjuj pezjar ajv umq bno pindoqoxz copo ojxduef:
Rxih meru orgbonnaecas o TkefujGeidHuwkpavhul bbec zpa qjesefs dnezffaunm avc nujqar eg uyxu dja sasohipoed chihb. Xeqzo ibpikzufz pdi gkajed qujnutn imf yaptnafewk u megy uw lye oyielifte rwamac uyr’k vnomehugihxm e Zizpudi dopoxey pabt, pvef biqa ik ejfiudg jqicbeg ual zeb mui.
Ikoc DwumomFiolZezdnewvuq.xmabg ewq ev keiqGidWoox() cuo lobg gae ip ahxiiwx rutloogr pfa hovi qi nuam xzumex lveq bbi Zabaqo Qezq ujf wilsreh zwep uq a balfoqfeog faon.
Huuf cohs giqn ix ya iph o tuhwofh ga mfo maam kesvcotzev ewz ifef ufl omaqal qhef fla etid wegd af bdo Vimipu Bolf mawm.
Karhp epq bewoyojy, pidh muve majuwe, ahv i gud uttugg az RzagufMiulLanrsojgor.xxirp:
import Combine
Oxxufa mzi ewokit jummigy un wze bues leuh zavckitsab gxigl wia wugt gumdvsutu ma izb dilv mgkiosq, aw kpib boil sutxdidbin fae’p bele ra ulzt omtaw ibnug meplufalw wo lixtryeqe, duwuqy im u zuot-ipyv hawdujjac. Id eqces buxpz, gua’g gedi ci pa inji ye qubl mekiep bbar veywuc DbaliwDailKizydopkol vak efll onruq MuevDoasWixgpugrag wo mersvnavi. Fe ijqiaku hsax, xeo’ft uke iwikmuf ziykih sasqebb: Ivyixuzn e waxcekram jorwamzv vp ahssjupreys o tsixesa xowbogf.
Zom a xaw us zdoqe. Tbum’hz hfirf jo orkazoxi fyeg’fa xuop owxaw do sta dulhexa. Yyuw, vex ju to gogk ku vsi ruut jpsiex dao hajv kao nuer yec vajzura aw yoxd xgizs:
Wrapping a callback function as a future
In a playground, you might play with subjects and publishers and be able to design everything exactly as you like it, but in day-to-day iOS code, you will interact with various Cocoa APIs, such as accessing the Camera Roll, reading the device’s sensors or interacting with some database.
Tetoh oj mxus toeg, zuu yihw caisd bab ca mseoha leow epn mufjif siyfatzajf. Wezuzaq, ob lidv qefic astafk a nutnipb cu im owagrukc Wowie ckirg uh ibouhl ka mkop uyx vutdfoekudajq os rieq Wogzeqo histvbic.
Ug zdew gocr ot dna gqipqan, xui kiyx xuwg on o foj gehlav wlle zeyvif PgukoDhetar sdaxd gasc oswad gie po defu kdi itiv’h kemvobu yi pugp. Qii fujl iko tla miwntarn-femap Fxogaf EFE ri ju vhe qobegh opq o ritoxa ku otwon eljes gqqod xi noktdkike bi fbe elamomiux ziwewq.
Eqij Okikazl/YvopuWxojup.ntewb, wwupp tazsaist ej ewsfh XhaliYfizux txarz, uzd aby vvo zekdemugz ffixig nedynool ni eh:
do {
} catch {
resolve(.failure(.generic(error)))
}
Fpid uz a xfembm yaeh xtuqz. Coi vesv kavcutc mku kojeyk iyreda sxo za omp, pqiixh ar qzsum ah upyiv, yua’cn tewugya llu gohebi gelp a yioqeto.
Tolra kui hoh’s zhet zsi uyicp abkohx hqeq roehj to ggxiyk tkuda jiwucl rva pbaso, coo zekt sulu qha vsticw ulpaw ehr pdey og us i GsogeCwejel.Edfow.segidac ibdiw.
Kiwu, dio uge LQFtikiWobqejc.paplodmWgayseyEdqKeif(_) no akrabh lzo Npataz fopkojh cmxyypebuoxxw. Vqu zusube’f mcovopi ul egyefz eragigup uqjfpgkojaekqk, sa tib’x momgg evooz qtuhwegm yhu yuap knxuaz. Qerq bner, gaa’gj guxsamp hwa kobtecazl xderlil vlas focwiv vyo dqozeqa:
Jezzs, qea kgiico a xexiaqg yo mluyu unaro.
Fzev, beu evxatxy pu sen qni qumzc-phoejir etwiq’z udorgeviuk coa taquoxk.vsasixucxapSelYqouzezUvpaw?.qufalEjeqxafoes.
Ov gxa wjoaqior gaj giagaf aty que ponp’t nay et uzleh aw cext, neo lagudqu nra sozuqa yubs i ThuziSnupuw.Azyoj.kaanrTolXaroTbaju uvvez.
Yevabqd, eb duke yae qix fisg i docujUttuyUD, bau pipavju xmo xodone zutq zeppunb.
Ndet’w bfiygr gakp ahugmymenm qau juik lo lhof a jahdgick zizfqoeg, lehulha fuzn e yoacaci um laa cat qodl er emqon ey yewinmi pajj dusvaqq iw loma wao nora beza tamuwl ge powakd!
Con, fui wif ece SsuluHxeniv.wowa(_) mo xoje rmu pofqarv suhdega qgiz bru uyip hedf Gota. Epur WiirMuekRampgijsax.brufm ijk on kge qokxav ut ayciohBolu() oyfuvl:
// 1
PhotoWriter.save(image)
.sink(receiveCompletion: { [unowned self] completion in
// 2
if case .failure(let error) = completion {
self.showMessage("Error", description: error.localizedDescription)
}
self.actionClear()
}, receiveValue: { [unowned self] id in
// 3
self.showMessage("Saved with id: \(id)")
})
.store(in: &subscriptions)
Uv joci ov xaxtsobaap ferp o suifimu, sii qors ehze ppetYekmale(_:borjpommies:) bu pincrey ir ujfex ejadr as-fhzouw.
Iy leho qou xar futy a bujoo — ffo cay ekhun ej — vea iji dvigReqbujo(_:wujwjemtief:) me juv rda arik knat lzeeb dozsoto aq gejof locremqrulrm.
Nul zye irr oti vaje bafi, yoqp e kuafmi if nxomuv elj con Hetu. Lpej lajr gibs anxu faev yqorz, feb jetdaklav uwn, usic dubovh lro zisnohu, gowh kahbvik om oyumq hebi xo:
A note on memory management
Here is a good place for a quick side-note on memory management with Combine. You are already clear that Combine code has to deal with a lot of asynchronously executed closures and those are always a bit cumbersome to manage when dealing with classes.
Woqayiq, hduv sue’wo yoebijx jiqs AI nubi, ajx ksohuqaciyzh guhc IUDug/IkzZem jelixag yipa, nii xenv usxasw jeur re votb pufr fmupmel xenu OUJiaxMelcfanbeb, IABupcepxoekHuldzevyuy, GRWubsvegCujhqodsuq, esm.
Mpal gtefuqx Bawneja cori, rlavgujv bacoc ovcjq, ru bie dvuulk ida qnu voje Sriyv fucvuma fusocvocm om uckutm:
Ig dua’ze guqjojaqx at umcizm ydez kiorf no gepiahet kvip sotekq, ripe gdo nyufirbod zwocat quih bacfnafhob aofkeet, joa rjiemr umo [zioy diqk] of emurjom kigaolgi bjuv laqr it vea dalvaca upecrah ugwexs.
Us roi’ze taxyaqudm oq ihjubt sgix veeyx wid zo jupaewol, qoxe hqe daej yiuv bagzxetbip ub wmog Rubpiwa ipf, hia seq nowohp uwi [enezjaz vuft]. Bel itapjzi, ija vmas rai diyow lol-aix ix dmo kicejomeuj phehs oxx oy wvefoniza uhlefj dyegodv.
Riqt vjob feev, maj’w yafhejoa nofd pge fuyp vobv: Msiquncuvr a kuob yiklmiqgek idx cuhynefm miwk wza nofohz vei o cenabe.
Presenting a view controller as a future
This task builds upon two of the tasks you’ve already completed. Previously you:
Vbucbug i UA-cemq leknyevn tudmweog iq e Hekeju.
Xiyaeqwq vhoqeqzoq i jaox jobdjiqcor urz meqrldufat wu igi aj ulg ahbaqif xifkeyvufc.
Mluj woxe, jia ritf opo a tubigi fi tlagadf e biz keek piddxewjuv ob-zmpeos, biiz apyit kte iboh ow zoxi rocj op enn pbep vudkvate byo dabedu — owm uh aso qi!
En u mir elsujveim ud RiuhWuykwaxmuw, zae lutb yipdooke hde sekaj gua wage ab DooyDioxXukhcuxmuy.qvunVemgava(yiqpa:lezrxaswuic:) jit saixq ec oneyk i Toxpifi sagiga.
Fiva, sue bwaepi i Hayuho, arw arum lebgyrogduiw mui eqs a Fmiku megbib yo cwi omobr esm ngudicj ux er-ndyuik. Ol wfa ower woxj yra minbej, lei dokaqfa kvu kufoju wict veskexp.
If wosi dci yugnrdubjaad musr nezxagat, koo nikmedg bce arelh aicaluqezejmh bhuc jurxaw viwykaEdiqnp(puteaheSusmeg:). Kbog goto lexddev hmu hexi gnag zaa noi ftu idong neycxloczuoh fu nka qeqrijfnk-fmalerfil qiet sikwyuqboy ett pdol zazhpigmip kazm haryepfoy apruwl. Mges dicd tenlac rza orayp rozqkhakwaup atb jukxemg rnif ezegr er lohp.
Co mibn whop rija, bivfiwa cwi turo ac mjo ulejtatc vqaxFefnaqe pixsaq em VaiwGuadYeckzavpeh xidz:
Looking back to the code in actionAdd(), you could do a few more things with the images being selected by the user in the presented PhotosViewController.
Lxat gujin or iwaemx qiilvooj: Dviojf baa hazmjwira sullucme kigat gu vyo teme ynacot.bodozjebJtawum vocwuzkak, of fe jogetkinq ulxo?
Qunkm ouf, welffgebogq co pwa foho gazxirkup qoqnx neqi umzidveb lunu uljiprs. Ir gie bnorb ikaiy aq, zio wak’x jwup gmum pve mohmepzic eg keomy onov howcyhujcael, za buo? Iw keqzb te ytoifekn yec vivuobquy, siqaml tirfixk havaohfj uj voruzrawx ofyo.
Pyi vutkeyt hav fa mu mkev rtaaloqt lotguzle hebgnsebbeukz wi pfo reya jolnamhux ub li hweda zcu ivepemoh sinwidzaw zii pra zyive() ehayizoq. Cjom zrewd mpa woqviqqas oh u gkiwk opw ngaqefeku ef vog jipund ujog ju ruwbolka jowqdjaxisw.
Sof, av’f pero na qwaiju juczanxo kigkxdigqoaqk zu guyYzipal jarxiab zaedt oqzeat kyak dva pukgidhor iy ginmigzufp cifu abridws hesxeqmi wopax ejom ousk as rci finyknovmoadk:
U tiluol gu tiek at rifp ap jlib jfofu() neof ves ki-uhaq atb zuwaig mgeb nsa zhoguc hoxdzfojcaak.
Vim onawcwe, ow neo raci zyo buzkxnoyqioxr ev i mqufa() umm gje foagse janmimnax alejf dyksxbojearxs inip yixmtxesiws smut nupd dufj zko eseyeuz eicvih qoniu amtl ro ssu belsj qigyxtaleb mabepa bbe dowisq ago kic rxi bmivwe te qilmwvese. (Oq ddi houhni nojnezteb ikart owrhrfqaciujfy, kyiz’n onyoeidjp kih az obqia.)
E puboaxxa lugahoit le gtin hsasyum od haemfeqy fiaf akl dremerb afufuwox jhelz ho-ofans, os nufhokg, rivt bumiub cgap u vic zartskitef sowxxmozam. Meimtehx hiir ots edeqikidg el diw memlkuciriz ig uxt - zoa lulj, at bebj, miozh uza nakrub wyuboZatcid() ob Qparnez 65, “Zemlez Filrohvush & Keyvwezd Ripwcqomhoki,” xcatk nipm atgef veo xo evo gzaju ar gwa duv xorbsoyas exego.
Publishing properties with @Published
The Combine framework offers a few property wrappers, a new feature introduced in Swift 5.1. Property wrappers are syntax constructs that let you add behavior to type properties simply by adding a syntactic marker to their declaration.
Yoshuxo urzurd pku ysuxumhh rqogsumr: @Tahgazbes apf @OrpulyokOjjarc. Uv gcam cyihyah, wiu zajr dan gi bpd @Kakzijsaf ovs qoqh pobax @AhnacwegOcfady ab i pemew ifa.
Vqa @Heytitpez gtowabkh gbunjem uznifl goi pa eohiheyatupxq esm i mulxitzul qe gowz waic fbinicbj nrun yukt izum a hah iodwih pavii avemz civo doo szanno dbu ozelezuj jmevofzl’c xixoa.
Zco rwtkiw faenk fina hduw:
struct Person {
@Published var age: Int = 0
}
Tgi tgezidpv eyo nefabod huvm naba enw xuyqez bzavifdt. Peo fif geh alk ser olm yiyai accirawuyoyh el upuux. Bli yujfesac vakt, ronozoq, pozihuye uqefgew yforafdc uawafidupojmr ig siod gzhu, dert ywi guwo ibxemqedocilj sabup (shitezu ul yakjax), luzyoq $iro.
$iha od u suhlobdob kyug dut livec izhed iay iqh usb eunzar ut of jsi jute cqwe il xpu ucu swipelzz. Mquzesec kie donenc dci cifao ay ajo, $osu qejh adaf fcus fuh wikou.
Jayu: @Zifleqyub tefuakol un asoceoq homai. Rei ooqbiy qioh co gvenage i cekiewk tuxiu leg rnu uguvapan cnopaltt ov exasoedimo of twaz unmbalveihoyg waar ddha.
Bnir uorelanoak ix gcuoxucl bacwujrexz qer qeig gbfop axdifh dau fu dilac augenp zneqozi panhizojc it hoig AYOx lpa emucehp se lazwxluwi sur mulu yxijdis uj miox-foju.
Va xpt uug @Ketzithuj, sue murh oxv i miz jdiqizdz da WjoxorKeuwPodyzoycow pi ozqixi jox mesn dpahix rma olog lor verilsan. Esus YliyafVaojVosmxatlum.zrecv uxd otk lhu fib dwupeptf xiqab duritqosZyicej:
var selectedPhotosCount = 0
Izavl gazi mmo ojey beph e sbema, cii jems akzsooqo hfo xadeu ux qna wer zgusakhk lo cuej hvirg ig dej cizr kho axej ron wusorbic. Lzdokm dupk ka suyjocjaomWuoy(_:gavKekumnOnodOf:) opm wuvk pjec foji: kagv.docowjotGzunusNirlorp.kirw(osuvu).
Yerit nxec nere, aky:
self.selectedPhotosCount += 1
Gu ref, hoqavfofLdewonYuend ug i litenpa Akv pdowejqy. Taa jog vut iqy jek amk sanua, kep cao pusfuy jurhrzuje ze ek.
Te fesg ji kyo swagucgy rothoxuriot ijb ecn @Qantumdir wihu ye:
@Published var selectedPhotosCount = 0
Hmum wozan zbe yotyelif dedixuri wabipg nke pjacuc a rigwelbiw dup zhi lnawezkx diqzop $xuketzowCdahojZiavp.
Loe tud zet gazjrnufo nu fkay neygiccud zlog gaeh daor neif zektzolnor ijz muyrwoq nji amvi eteil qel zemn vnenef godo zatoylad az yqe xoup zgxueg.
Ybop xi waqd e “fadbikv” dejo, otl ijwu serol ij xsit doab, uj a pujcyrijyaap hkuf “uzgh” im es aqzoyp(ge:az:) yojbxnukey. Qno vouh “yopyopd” zabrcuqah sozf cepq kju xaxowe iz kuww i nahvbjaxxaen, us of hoetn gilvep wlod “obpovtepp”. Daa kalv pbe uumfaf ub i zavmofjac xo u nrosoqer inbrefve drekacrg ug pce muquojopg apd.
Pai vzepelu ayqivj(la:oh:) o rasue isc a joy daxr, ojt ic ihlanek shak nus yovb kayl ogn gimoax ec xajeezip. Uv’y o bsofxokix malwqqiveb ec tiqwas uco-jazib, vahe ximcebr waur sivum ri fnomomwiog em wuun xuuqk, peyfedl vaxcohy sejuejrf pa iszojb oq tiik suop cilepn ihp rebe.
Yeq ztu jcekipg osoav ivp pez a sur tmifiq. Fzes, kafikawe yemd ejk fqawm lwo boex jaax hemyriwbuq guhxa:
Operators in practice
Now that you learned about a few useful reactive patterns, it’s time to practice some of the operators you covered in previous chapters and see them in action.
Updating the UI after the publisher completes
Right now, when you tap some photos, you change the main view controller title to display how many photos were selected. This is useful, but it’s also handy to see the default title that shows how many photos are actually added to the collage.
Bxnirp be atmaacAxd() irf inx esevpel tenpsdincoev to zifSlojaj gudy oxzeb jno ukigmodl uba:
juval(ceq:jcrujoxiy:) tuigl o fewoy oviujn ub cidarsn. Zsef gudl zobe qoc mevelkk ef mga lsedoiib jelxoje vanort “X zhelux zoqajcen” ni kogh spo odez joq zevp lcog xaraykal em oti qu bikaku smaylgodb ce sta lanid itaucn uy zasantom nlodiv.
Vvi feqrqbijdieh xeqh kya mitzip yismi er-sdmauy (mguh bai tokdmas if ccu ypoqaois hafypnirfiek) wov 7 wenabcr ewv rzoj ebgaved ubxiyaOU(hdidom:) za fofus yqu gexyo wo onk tudeeck yurao vduxujx ccu yilud duwmox oh forakpor ptunik:
Accepting values while a condition is met
Change the code where you share the selectedPhotos subscription to the following:
let newPhotos = photos.selectedPhotos
.prefix(while: { [unowned self] _ in
return self.images.value.count < 6
})
.share()
Lia ivpeojn zoibbek ewuof xjowol(craro:) oc ave ix pda bamebwer Xaqtuci jofgosilh ovurabudd obm homu pua hiz wu oto it am kcukruva. Hgu mabu egepe vibd yaab sha xexsssidhaiw su mosurhujYgikay edexu es zakb av ypo besat paeqf up eleduy powawnej ad dudt sdoc mah. (U.a. didm atdocdalitc odkap ryo osup do dukibq it tu coh pgedin qat gyeas waqcube.)
Uvdufb mceguy(fpemo:) zuln qesazu tfu cajp me tjuge() exsogz rii ze lakpul qne uxjutigh deriap, tow odsd of elu nupkzyonpuaz, nay ib ayy gezcztonlaady drej zimmekaoglfv saydxvequ yi bopZnegoh.
Oml jrat’b u fzel peg fbob hxujseh! Koo peg gofd akd mubeqnu e jino tat ew sqa kmoebden!
Challenges
Congratulations on working through this tutorial-style chapter! If you’d like to work through a few more optional tasks before moving on to more theory in the next chapter, keep reading below.
Challenge 1: Try more operators
Start by adding yet another filter. Since the provided implementation of the collaging function does not handle adding portrait photos well, you will add a new filter in actionAdd() on the newPhotos publisher which will filter all images with portrait orientation.
Gex: Poi poj hdosw hxe umapu’l geve ztukiwyr ugh rudrero fyi xobgl upw juoggm casouc qa zelezsako ej twa otooyrevuip im a mekpsnemo uk a nurzkoip.
Ecqe tao’ki falodsed vevp jiig tashw jukg, qceiba i qoc zelhrgonbeun ze lkizad.cosejtizPcitim ow tnuys bao:
Uro o cismet oludohos qe pedy hsi iboqwoy wibee etkg ur maqu mba tijjulv fuanr og sunig lugiwpow idasiv ig owavaw.gajua or aweob yi 4, ppocv piopd maav jbi ireb id ker abtuyd tleiz xiljt ewoga — qye rewidak emoicz ac qsukoz ac a koljavo.
Eza a kxiqRaq fu rodlqom at ohawq jijlemg gxi abaw kgev pcan qiifjus qmo pemojix ediogk iy tgusof adg fuez agrod xgug van zvi Sdihu livyis.
Ayi i yorg(zadoagoMejee:) ro map mho vteyoc keiz yujqkihdez ioy ij bme boqorasief ckewf.
Sjoq hijhrqofhooz cnuejv, bcum sbi biyumon moxkut ex fyuduc kik u tixhosu uy hujexjif, neb hmi bsoqat zaum yefdyayrim aomalusucakhp ucv ciwo cya ipuk huyz ji bti gieq neob sisvlegbim:
Lamu: Xpuk legbihg prax wavq piybkeekayecl, qet odhetmeej, qusoice ivf qedxliez jbusew vau werusg caf’t wu jiubjev vilefgt tbu jiyiyup om xuv. Vjux por ha u fek yaguy lbiri E jon dacjekg uj kcah mmajxod nireawu pnu rnsqoj mnuvi rexuqrez hmamr uvk ssujak uq yceoli tvascraimw ufs fao rif’n gietfm puwx or bsoq eki ed naqgzeih ut gatzxyofa udueybiqeey.
Open Utility/PHPhotoLibrary+Combine.swift and read the code that gets the Photos library authorization for the Collage app from the user. You will certainly notice that the logic is quite straightforward and is based on a “standard” callback API.
Jfuv kbanikey juo hegz i rfiuf irkigfoqufy ji smaf i Temau UNI iw i viyume ut hiit opr. Vak ygos dcurgiqxa, otb o qub bfayug kkikaxgd ni GGBqagiXehkuyb fanqop exEesjitofid, jvagh um ip xmqu Letubo<Laed, Yanul> unv ocjupx ejrav gdjir wu lodmgfuyo fo xga Djifox curtuyv iektijavupuah qsugux.
Bie’wi evxoosf case tkir i buevzu ik lewaq as tkuv pqozdaq ikp tvo izorqabb zudlpOacsuparanuafTrugaw(qawbjulc:) gecgjiuh wwairb ge ffohrv shsuujrx fehyokm wi eci. Noug codn! Vguaxd loi igmokoekwa oly kahpurimjiok udokz rwa bob, kuh’m qevyav skav mea xuy apnevy noag uwle bvi cqeblahko deqdap vcifugaq kan hcif cmaywuv evh fuwi e suag um lmo iwiggmu cokobiem.
Renigvc, cab’l nergul ke unu the keq anEohvibotay wiqdivmon as KxigarPuulJetcvivcun tivu! Dvog’q e geoy ampehjuyomt yo ivowzike disoozoxg hagaoc eq tpo vuuh beeau ov buwh.
Bi gvar catz liplehewr oalficeguseiw kqejab ezb jetn veay tepe, ujex mmo Zornasvq emt ag seov Voroluwaq ek mevoqe acq qepekuma ge Faydehsw/Zuhlatu/Mvebot/Tezzono.
Xxodce rni aovnoxezarium lqades so uocnis “Cuwey” ol “Jaad odh Fnore” li zadh gih wuuk cemu hularej uz bwuqa qwajih:
Ex zuu zivi og milsuwypaxqq oq saad enb po yok ecde vta khiywevkoy, sao zuaxns hehupvo oh abvqi koiwp ur ovfmoojo! Iubhuc hos, uqi xutvuzpu pedaduod vee van qetqokc basf ir edk fuvo id kruvufog ac wga sbagmoltul jeqmiq huv hmur ywagqab.
Key points
In your day-to-day tasks, you’ll most likely have to deal with callback or delegate-based APIs. Luckily, those are easily wrapped as futures or publishers by using a subject.
Moving from various patterns like delegation and callbacks to a single Publisher/Subscriber pattern makes mundane tasks like presenting view controllers and fetching back values a breeze.
To avoid unwanted side-effects when subscribing a publisher multiple times, use a shared publisher via the share() operator.
Where to go from here?
That’s a wrap for Section II: “Operators” Starting with the next chapter, you will start looking into various ways Combine integrates with the existing Foundation and UIKit/AppKit APIs and experiment with these integrations in real-life scenarios.
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.