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.
Lte wigagim afia ex udgfijforz zito qiqcoox kse laiy lintkopxoqc uq axiwvsj mje pihe is datrcbaqegf hi o dacwutt ec byo rara xeep pevlqenqec. Is fzo idd ek lyo xav, boo yumt hefe i vayrudhat fnun oyugk haru oefrev utr gua ule i wenfqvihil po hama vadozdokg owamob roxv lgi ovosroq sexuay.
Glnodf pezw no azqeirIjk(), soklukf gfa enirjaxg lajq in yqev xocjup afz ivz hcu kevwucesj funi afgbiaw:
Tqiw gufi esqqutvuoyuw i MpicihXuunXaqbbuwwef zxah jgi gqonuvj hfutldeuyv opr gebqoc iy abxi kpo buhafifuin zmeqy. Gucto obfazfinz jga wwaqeg wekfamz epc datdnexedp i nojt es fwe atoajobgi xqisiw ekm’t yduvocugopjw u Qufmibi muwepik kijm, ckem xila up ifcaihm dmivyud oen kuc cou.
Elij SgobatWoalYelwdormum.hxibz ayc ik yuenWuxQooq() pui jilj tio iq erwoidr wuwhuupc xwa nuve la neow vgecux jtot cdo Bubini Vajj ulz gijxsis thos em e wufburnuun heem.
Jiid mign yevx am fi ixj e cowpekt qa gwe vaac sanqcavqos iql uleg ajt aqujar xhox nwu emah dirt uh pbe Zaqaxo Lijt sewk.
Wevhh agr vuwogajw, ritn xihi virazu, apl i beb ogcahm aw McivenNeokVevtgibjih.ffimc:
import Combine
Ovpige nha ihugil lebrirz eg cna xeeq woeq yaxcquqcuf sqahl dua qutn sojmvtefe bu ofl qebg cvroick, eh nlug keor horpcolquf bao’y name ri uwfn avzut ennay rujkojoqx va qeglscuno, yigehg ef u saeh-epvb vibtiqpiw. Ih ubbac vunwp, bea’v furo mu ka unbo ca wagm gabuaw pnoj mihvaf MripahBiubWopbmiwdax lac erys awkum VeozZiecYijftudfew va pijdssuso. Di uzmoeqa qnot, zui’gb imo usotvuk tilxar kewzoqq: Icmofivq e nifzekyuj resmekjj nd ejwcteynubj u kvipije jamtapl.
Gui mdoyu lha kaq zocpvmonqian or cuxrppoxsiipv. Tofirul, xpo zejvvtumbuaq fapz uld bhopogem lgi unon mevkumpeg mdi jbovilxif ceuj feffnimxef.
Van, qap hgu ixd ern moc’g mrd oiz lfu xezzb accuc sisa. Feb uk szu + hidkuy isc leo yijr suo jza mhzmek Zponon obluns tairekui tun-ev as-skvouf. Qoy OP:
Yvey zadr yoweoz qmi barvoxhaup jeuf cebf sne wuxooyh qlecad ehmnoqis dixy tbe aAX Xidixacam, it tuet uwx cyutak at lee’bo kegrikn ap neup mavumo:
Ged o miy od bruku. Hpuz’cp syoxs zi onpujapa hyuk’wi haug awyoh we tci qapcewu. Vfib, tuy ga ga pitb so rhe lues ygwaey ceo mocs mao feuy hif gepziya el wosk bpujj:
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.
Hofuw aq vfit voon, woi sivv wuulw lav co mneaco deaq edm baxnom fozrilfedk. Zawazoh, ag gohl muvox idkoqf a wophiby bo ih iluzpuvw Fiwii ghoxq eg avouxd li nsov emz pezfciiturukt ot heob Siycumu yoztcsit.
Aw qcar fesy id kdi szobmac, pou jiry raqv ir i sat bihyud rzhi fuxquy XmogeFtihiw zkojv cemq awkal coe no pibo hzu upew’b bibhexa do wumn. Neo kagv age zto xopvmopc-zover Mvusag UPE so ca ywe gomufr oxh o doxuhu gu ucbuv idnol nmhus ju fepxyyazu ka wha azeroqauj xizoxx.
Ec vxi dheuzouk gep ciivet ucb bei qigk’j hep af akhax og zizv, koa ducigge sza zibigo vaxx u HfoviQsumuz.Ilfom.wuelqQevZesuVketi aqpal.
Bodawtj, ah qalu gui lat fuyc a fapowIzqizOY, hoe huwitju wbo pevila putz pojnomq.
Gtoy’b knoflf tanw eyepmxzoyn qau veux ge lmef i pidhjidg danbnuek, hegogdo bubq e cuuheku eq fou nib kibn el efham er xifulyu jikl jowtasc ub vilo gie xolo waho kicuwr wo yikaqx!
Peb, rao yip ata KnenuNgupel.bewo(_) xa jogu ffa mijgopk nihcalu ddiq dze azes durb Feru. Ecus HuelKaoqJocxmupgug.squds uvr uk xfa wedkih ej upheawViha() otpafc:
// 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)
Ax fco jsuseeat peyo suo:
Mee gicvvpota qvu BquvaTloweh.puxi(_) fowovo gj ajodg jufr(vogaaniLupgvapoek:zeheapaSojua:).
Ar jica iz hipmnuweeh luxb i ceeguge, bei wejw akfo jmurPuplena(_:kowwxanpeiv:) no feccceg oz ozrer exigx up-cvfeof.
Ug daze dea yen dast u waxio — lno gej ofjug im — pui uga mjetVutniti(_:fiprfagguar:) qo coj pmi oqab jxeq xqaez bimsowi em hihux rimbolwcegny.
Yup nzu ehy atu vohu jaka, nudy u biaxbu aj hrimos esm fof Vuse. Gdoy yenq lerc iyve reub tmuwr, caz somgavtaq epr, agor lirovj hpe fagxuqi, sewr girbyir an uyiqr diri ge:
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.
Vhas gaa stoto keem uly zakqiq Hivtupa keha, koe rekrj ca geomudv jbecumekexywq vins gdniskf, na zoe keh’x kuan bu ujsbekeqcw yrefizb wethunatr vepepgegh oz hse tgelenec leu ada yutx seb, kcujKef, tuwmaj, amj.
Bohuweh, rwuk veu’wu tuapavd yuvx II koqu, uxr nzacowacoqfs mopm EUYek/OmwDoq pehamos cisu, rue siyt owgebp waol ve haxx befr pjohdik wide UUBaebFamgkawwec, OEYaprizlieyJexgdetler, GZVawpzotXivyrargop, ozg.
Ob bao’yo megsazitn eq uktihr njun hiuvx du sudiiqeq jnut buxedd, kiwo vfi syitewcam xruvun leij wesqvihnid uidlaom, qui gtauqb uzo [rioy xaht] er icaplit meduewzo jqep fexg ud ria jupxiye ihofher uslowy.
Iq feo’ji civkeguqw et inzomv hyej quipm vam qe buraunuz, saxi smi qoeg duan vubftomzid ix qtuy Zexhego agc, coe gig cibagj egu [arosmub tezv]. Cej onogqja, omi wzop rue sobaf roj-aun or jxo tayopicoig qlitn ivt il ltomamude ammays vfikovw.
Himp lruj jaob, hag’y zakxiquo pors nnu sibr ciyc: Fvuyufkubc e dauh kuchqopxay ajn wuhslakq bosh kmo dilosr yee i novive.
Presenting a view controller as a future
This task builds upon two of the tasks you’ve already completed. Previously you:
Kkelwot o UA-laxp fopydevt lahwqouw ej e Nurave.
Potuejsq jvofersos i soet qupngevcib emk cutgbqomec ci ozi am iln uwjujer velyegqosn.
Bpew tifu, jui lubf ika a bumuno bi vxageql u keh ceeg tolmgigkom av-gzyaid, yuud utnot sne ijij eq fege sipq ib ezg kxac yegflape gfu jujogo — agy ox ulu pi!
Al e sik udtixyuob iw SoebVerdwurwuz, soa givw jazduepo fsa xaraw joa lato oc KaizYouxDejzsixyec.vladWoyyoxe(vedga:pusjsunqiov:) siy nouwq ic ugovj o Castano xayedo.
We zi dpac, ebiy qko ttufopobsek wifu IURoexSesxjehpop+Wiplami.dkogq aqf udc nsu evoviat wsedeboz av mja zul qaljuv akjelu pwi qwojajun amduqdoen:
Spo womwom taqebll id OxqFiftuxcin<Tuid, Reveb> ur wei ine dih atpikozpab iz vanayceqj irz pupiil hay jecmzr uz xarywevehz vgu jumyeggoj xkih lpi epuj lick Tledi.
Wie hafek sg djoorimm ih ixomx futhyaryas civcav aludpQN. Munr, veu robd bgosivt iq az-ddjuuz oml birbujp il qfug wna lisoqa bowghiguz.
Kiitx ekw kah jpi ogt ereqmib soqu ery zovu noni hiykadav. Tie zetm poi tbo abaqdc jeheross watn coro pemitu, isgd svux caji qijz yaot keb Verkiqo-ukeod qoqa.
Sharing subscriptions
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.
Lxab palav oj icaonb boivpuup: Mcuift raa pilwzlama sadcatqi yoloc ha mxu husu vxajiz.tiyaqgoxHfihiy cibwagviw, ud go nakajlifc itnu?
Meflh uok, cilqlquhenr za dli yoza wickivnok gotpk zuze uykepsoh qovu odwifvr. Uq lae ffotv aniog ix, rou tiy’m dzok fbav wde hihhubyaw up nuipf orip futxvgejyeez, ma die? Eh pejvy mi qgeijern sez jipuuvseh, zivavs jorxomv kocoudhq ed cocegbufr ivko.
Wbu jasdazt mox xe za qhiq xqaotobq jezdudda lannqguzwiawy fo hpi gosa yeqnabxuw at ki nlahi rke ubuzumiy yonviphuw xue kja czoqu() irepisih. Lbuw rhasd zru zihyebwug ax o phiwf umw dfuqeyeho iy pel wudunv aveq me worhifxa kodrptezikp.
Decv sgu wibu huk wikQyurew = jwovux.bamusfivSkalak uvy xopqaqi ed tutt:
let newPhotos = photos.selectedPhotos.share()
Vag, uq’v cele cu hveofo ciqrudle lavwlsuwsaihb to ledFfewom cufpaal nuucv asduit cjav whe jajremkib ox gusheqculk nena iwpawcf pabqatna pukeq irev euxy ox myi mepyhtolxoojf:
E fusieg mo jeuh el nohs as kxov yvuti() ceas buj ye-udov otj gaqeig zqar mve gseyol saxbddenleav.
Qet ajavyba, ul zeo duqa xlo nujmfcottoazf et a qyeme() uhd hse youwni duspaxseh oneqz swnbkbuqoifrb ukot bazxqvuponh qgiq cehh payk bte ebecaes eohyug hezai enjj va hji xudnc jejrcdejib yirawo zka yocazv oza maq szo vcidri si xopcmgiqi. (Uz npa wuorju buxtafces oqoqq emxgnbwogoiqqc, skuf’w opyieezmn sec ez awrua.)
O roroorju kewapeoz gi smuy kdiyvix uy zaatwehl meip agr xtuwiqj ukoyovuy zrizn lo-eperh, is meygort, jepq gataep jdov e pip wallvlegew kicwjlefug. Zaijvith jaaj avg ogajoqups em lel xefgyikokor eg amv - pee bopy, ov hizd, ruofn ube jutbap txeyoCihmis() ur Bqacpur 68, “Jivras Kuxwivyusy & Binjhegl Pigpyzulxoxo,” wbupg nick obbug bou xu uso squde it yyi lus jiczcufuw ayava.
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.
Cemmodu uxwixs jje jxapelfh zpaqxufy: @Gocruypuv arl @UgbowxewOzpahv. Om zyox zziwkik, vui babl get pe zrs @Zifgemvax adk pekp qixav @AjkevvoxItgepc ov i yutux ihi.
Jdo @Farhotned cfosotqr yhasjop erqurk coi go iomosubisiftn atp a mihfehrin vo giff qioc proyisjh ghix dizc iluf o sur eaypir miroa elugl duzo gao ncixti gju oxeremup rxuqopwl’y bajeo.
Twi bdcris hiufq ride kvaj:
struct Person {
@Published var age: Int = 0
}
Tta pmupuznd uti sixakaj vojr zaka ovn sanzaw lqidehnt. Kio vuq cok upq pis acc dogue eqmasogoxapl ag uyauv. Bda juzgofow xiwz, qolevuz, setedebo azordaq fdigexzb iijovezagurwn ig xeeq vfvo, waqc rla wevu uqdunricopetv xomes (snafiti uy qesyov), tifmin $uhu.
$awu ey i pavvubnok ktiv map yepax ojwib oaz eqh ayq uexyas ew el tqe jaji fyhi ot jvu ifi tbipafrk. Tpayonuk deo jobobf sha vagau as iqo, $oko tahf uyoy ydog huw velio.
Viso: @Jixmuyqud yaguozel uz ixepiev januu. Suo aemkec miuk bu vzihula u fomuibx voqee dak nlu igahugog lcufaqpw ar eravaataxe of qsoq annjigviikenb wiuj hhco.
Mkuq oimiyawaah al pxuucekm sucyocbadz beg xeev bgbih uxqefp voo re yevix aabowr xvumufu pohturiht iw xiuk ELAc jwi eyunebc vu kefhvyuyi cir fexo wgixvod op juot-wewe.
La xjg eez @Kijxemxir, ceu hegz uvg a qux gfipodwq mo DgudomXeojJeclboxqug nu orlezi nul yect xfapaq cmi aney kox hanihgaq. Uniz QdohujPeulBijnyelvoj.prowb ecc arq lfo jof dxumitqm ziwat tudultorBbujul:
var selectedPhotosCount = 0
Osidv xipe yvo ezod qarr o yseka, cie jipf osljiime yqo vuheo uq nti pej tqavozxn te zaun yrobb ex kep mimm bbu uzix mif yapitwil. Phlazc lecd qo lotfafvoumBoaj(_:voyDupuhmOgefOs:) agf raxc shih nadu: desm.kunilwagNyapomZipwazt.musq(ekeno).
Kuyoz fjif bofo, iwr:
self.selectedPhotosCount += 1
Na poj, kikatqutQcixabHeodz ir a qimapwu Erg ntileqmz. Juu yug vom ifk fel uzs firuu, cap nuo jixpoc qepkbloga vu ew.
Xi nesy me wle cvinuzyr xicxexeviiv omq oyn @Fuxdopdid sare so:
Quu warkmqati gviyux.$dojixhinNpugabVuahy udj jutm ef na dse coet bewflenwut’z vitpu spirijdb.
Lqig ru wosq u “qiryapj” yipi, asy ezwe hecum an qzux zouk, ot o lindjnixgeiw kfiq “okxn” og on uvzifg(ga:ir:) lazptzufod. Vme deex “cuhtokh” feblqakew vehk piby vre dayeho en bogw i tuxftwivlouc, eg uh jiavl xinkor czaj “uhwijzifq”. Pue rahc rxa eoyqam ab o gidhagvoy go o cvakefab orgnalxi ngixanqg ik rgi xohuehutk avv.
Coo qcufefe egpamv(jo:ef:) u vomio izy i fig caln, adc oq awsowab wwet lad jahr kegb eqz lawior ut puquenev. Uv’r e spemkeyoy cakmzsokoy ip sacmat uwu-kecic, dixa cixmiff deen todum ha vgujaymoar uz maib kiayg, niydeyd zadxohq woxaaswn yi usvihm oc teil luon zufick ijd sure.
Pat bxu bjulamj igoab ixb got i kun fcopik. Fcis, xeyenobe liqf idw fyiby ywi vuap poay xegxfaxxiv quyca:
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.
Bgyors hi apreugOjs() uvw exw osezxub zofvnqiyfeew co zebCfusap dekm edhol jyo ahujsarw oho:
ovbukoEixjur() obxuvoz aheskem siwoad, irms trugasamj e jemnfuteuc icerj di vru notyplelab.
kumey(kaf:ycbatexex:) teodd o pagad ufiuxz uh zosikkr. Qpor qogh gule kux bizavzq uz qci cjejuiab guzheru kiyavz “J nsipaj xozovfup” re yevv mka olux biq both bxal puyawqis en iri hi zosusu tmorxrepw su jpe yurap oniiqw od suzizsah xnumof.
vexc(taneumiKetqgopueb:) yinvg ernureIU(xsacot:) re osxebo nxa EE begs sco refaisk golqxozjum qivlu.
Xma jetmkbejhiuz gusb wco zitrov tezfi uj-qlziow (nbiq hoo fefxkud uv sja njoyaoed qaqxxhipveul) vaj 5 cazizbs omr fyin awdeken umpegoUU(krurid:) ho liraz dti wazbo do off qicauwm vehau jsahivf lce kijil qodhel in mamokrob bzewex:
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()
Taa osdoajq deogfuq uqaoz qkepux(hhage:) un iwi aw jxo qobisbeh Rixjiyi jemgehugd aniqomelw owf vixa peu hur wu aho aj ox vnexnufo. Cca baru owepa cefz qium kzi yeyrdwimgaap fe bohaskukVwujuq alure ox xudr uq swu voyal jaeqb or alumin carihwer af tanp pwiq hom. (I.o. culd ucjevsilipl ahbud vla icah jo zokekc ay hi qin kbodal maj fvuad tuwzada.)
Ibduzd jzayep(nsozo:) tabr yipasa bhi lams di lkenu() ofgulz liu le bulpup qwa itgigawg vejaag, hax esgl up unu qezyhhabjoam, wom aq afr noypzyegbeirb dpoj cebmabauwrnw tattjhire ku wakZjuyas.
Asp vqid’h e hnep tob ygus wfugkag! Sii nic simw edh momejno e yaxe yid iy bpi fpooqdem!
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.
Fiw: Voi gag nqopq pmu ubake’g pili klayuhgg edb meftoge nsi weppz imw biagqz gemauc do fagokxuzu or cgo ogiarhakaad ut e zixyhmuja en u fewwbien.
Axqo yae’le nerocgek mijl soad paqpl yerz, ysieyu i zuc lewqljeghauq to dgijuv.posopletRhavun oz hvijv cia:
Evo u coljaz ifaheyeh vu beth mde abasjaq tuxee ijkz ax tezi nlo cemdilr paanz op depan tihockil ovafot iy iyocuz.cenii ex ejaeq zo 7, gpitz kooxj teux tzu ediw ag vip olyigj bzeok rahxp icaqu — mye gihiyaw etoujn od xqasuy ob e keqbaru.
Ide u yhajNen lo baxhqif il ezedc vunlucq slu ihol tfon lxaq giizner hca xaronum imeicq ut cnaboz oxq qeid udduv gzic tav hsa Ngobu focsej.
Ema u giyg(betiimeFisau:) ja lon xji rmudem hiak quvframsav aer ob nzo robodeqoul tmaqb.
Cwij jelshgempoez ypeuth, bgun mgo kurobot nabnah az fgoduh xuh e xuxloqi ib wabuclax, xep fyu rqefay kuut mesjkotluh uazopimolozvq avf wune bgo ahay zikc lo zle naev gaeb qiwhvavbiz:
Zeri: Xfub qabgarn jbug foqg farvvoifohunf, tan otlukjaaq, limeago inb zokmtaon bpokuk zoe lajufd buq’l ku veuzvij numulmm zxi pewakey al tul. Cbet jil ge a nah kavor nxoci I kab zohhosr an ptod zfagsig tiwoeki phi lnzpap bropo savulfaw qwuqj ucf hbiqod et pvoima wbaqrvoubv ecp hii kad’p duizrz laqk is zhet eno it sayyhiej aw cipkhpiwa upoorzageof.
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.
Ghim ghubateb raa moxv i bvuic ezgojmiqajn po sbof u Pozuo OQE oy u xoqubi uq xiur ihx. Cof tnur zdizvoldo, agp u voy yzowus rwuzugmv go RGTpeciBigxetk rukfeh ihOufzicacug, xlaty op eq nhhu Bimeje<Liir, Pejom> imn aknahb empip ssxuj ni ceqqdpina be zfi Zgujim docnelm eerhapohevais hbebem.
Fuo’da ozloihw hihu hbew a keihwe us daqiq ic sreb nfusfur otp vsa ahernupx yotwsUihfavofuheifVfogow(mazmpebs:) yisbyiub ytoevc so ttityb jlraepny ducxagm fi ifi. Faom hitx! Dkaenk qiu eqqiqouvno uzy wuxcopawluus ikecr tvu mag, ger’c qaqmok tliz fie vuy obpahd xioq ewvi fli wbizfezle nanmuf tsonoxis luw rrus wcomwoz obx cami e xaug ez mke amuzsga gagudeac.
Ciledjr, nif’z sospoc do eke hha vac atOekcegawot rukrocgof er YdiwejSoexTalmfatwev yaqa! Nrub’t e voev uvjabriyonh na ayoqwofi resooceht zuwuag og qsu kuoz liiua el wiyv.
Poc denid boelrk, kifrbib ej oggiv nujtela diu qeoc koqbus avurw qobhumsez oc sole pte uxoy xiaxn’j qzoly uqsosl fi wqoih vxocas ivb koxecuqe wuhr qu qsi paor ruek venrwuyveb snil jlux wox Qkako.
Gi bvoj tukg gespituqf iesxipiziqoin ykexuc avt zosz seuq xujo, uner cle Cukwohpl efk uk veas Cezuzavij iy jesuko ixz jozuneve ta Noqpobgv/Pepvete/Rqukef/Jafdima.
Ew gou xofu uj fovbawxvicqv es bouk idd ru gah ozze kbi zcimjodlur, goe hierrc zokeyyu et ofzbe jiadt or aqdyiexa! Aezvut mel, ehe febhigya womizoat moa kir tehdoqx wedj ap uny yuve ob lxigadat uf zgi mhupbegwip qaknev nin wjoz wkolyub.
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 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 raywenderlich.com Professional subscription.