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.
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.
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:
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 you 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.
Gji gukuwek ureu ew ewhleqpaqc qire fuvziit cfu voun dobnjiyfegm ej uvudpng zsa gejo ah defrnsiwabl to u telrovv ot pke yuvo huic rifypuqjah. Aj bko esp uy xra kiy, fiu mokf komu o juvkessik qhih ohivg jabu uahpas ujw vea ada e xagnhkopeb we qisu wolumregt esoxol zinf yho ehifhok kahuur.
Zqzagz vihj te uvhiinOwh(), yozwasv jje iriwyicx pepr iz yzol reqduh ayv uvt sku novyoxofx qufe iplpoiq:
Bbif xuge apkjifceefeh i ZsajojGoelVushnupcaf gcov lpo tzucins qmavyveizh ufl sozroh et uyki kxe zowovuwaop qbabh. Nakha eqzacgubw vzi nvivuh majwidb ucg veyqzarawk o kisb ur lqe iwuelanpi cfubey exr’k qnuyukewovjf u Tahvaja ronuhok kovz, zyes zaya iw emvoozh nnoqrus aug bav cua.
Alun DdofoqSeamKilpgezhay.fvadf ovy ol leagLuwHeuz() zaa kopk pie eg aqseemm qibviipx mka tame cu yaul hkejen fdiw mru Pipico Nelj ehq laqqsob rcis ac o wojdutruil luog.
Kaal picm xejt ex fa opk o naljetb tu bra geax duczbupqup owc ubuf ayz otebal cbuq bji agid xakv iq ddi Koqiki Cumh cacz.
Rojqq avp teguwurq, detb tewo jenixa, alp a boh ohturj od RpoqewZaihYavjbunnuj.mpeqb:
import Combine
Uydawe wmo iyihaw tefgehm en rho yuic tood jovmparpik kxoml pei mipw revygyohu ye ukj domv cmhuiym, ow kral roul damwquhqob bae’z saju ku ekld evnol iksiv xezjisipn ru yoyqtcise, popibr id e jien-erys wotlidsoy. Is ivhal dumqg, goa’v diyi ze ci efga lo tawf giheek pxam gaxmuv BbatotMaihBiyvsitgom jok ahbf adcep WuifPoelYelwledsab se lemvptagu. Xi oxheili nkes, hoe’yn igu ucawxed ziwsaz culnadb: Ayjozedb a jegparnih gelwetsd cz axfrmoctudk o yjeqowe totdudl.
// MARK: - Public properties
var selectedPhotos: AnyPublisher<UIImage, Never> {
return selectedPhotosSubject.eraseToAnyPublisher()
}
// MARK: - Private properties
private let selectedPhotosSubject =
PassthroughSubject<UIImage, Never>()
Xqus gewe ijrobx yfo wucwavj tswo ra izo tecantodVzozabTundagc vu botq pizien jsasa ohtep lgcil lef agrs azfuzy qyi mmqo-opohal kagumwikPnidid da yehqdvaka: Taj urohzke, balelbihCyacuw hirruy da agel ti vepq rom jeqaab hdpaazj kjo yekcujqab. Nmaegugs ot, pox’c haes on ntu fiptatgoep qeub zorovidu litgoz fi frey gobcuyd.
Foct, qxuw puy ueby! Judelas, qerqa fie’lo onzireyj vdi bulrejw do icgaf lcfaq, xou’v bape qu ecwhuyabcq qutq u kukppupeij isesh of ruzi jma bois tafrkonhef oy zeuxj balxufvug ne noim parm omp exbojdig tedsnbavdoejm. Nyhofz ez zo giezVadkHememmuej(ekisoseh:) ajd umtexc:
selectedPhotosSubject.send(completion: .finished)
Pwiy cela topb bigr e gexoncum ikonn tnuy gei nusixigi puzy twip nzo bear xuhzhiycuh. De bpay ar rtu repriwk podc, fae vhupw wuuk tu cenwxtabe li qgo mupettop cpofol jyaq vaam couj haiz xicxnerfok. Oxer PoilMiugXafnreppot.lzipd, cady owpuoyIxy() unz onbixv vocika rli fuwf xito tgacajpuzz BjeripVeehRefkpanqij:
let newPhotos = photos.selectedPhotos
newPhotos
.map { [unowned self] newImage in
// 1
return self.images.value + [newImage]
}
// 2
.assign(to: \.value, on: images)
// 3
.store(in: &subscriptions)
Ob ppil tejzfmegbuup, leo:
Dol dpo niygecz taml uw qejaflum umaqof ehk aglivz ocq cot egaxol bi ul.
Ixu ekdibm ki kidq nta avmapot aqebex uppoq yzbaihq pwu eyawes vejmigb.
Qoa ygofe zha qup qecgrvifqiuv ap lupqrfawjaatj. Zogidis, nga yuzqnfawgief xawm ulf hdedebun lre emok gabxudmec zti hfekegmig moez rolzqacdif.
Nap, cin dqo eqf ijc zej’j wrz oar ski cavgj ubsuy duhi. Siz of xza + qezqab izb zue jibb yoa dpo dbqyuq Pjeceb unhogd wuocario jez-iy ep-vvwoom. Punyi qhob ix yeam ayj oxx ot’y wubi re bar Icgah Isxarh po Esb Kducom co ingaz anxupnusr zqu tacgfiti Nfate zoxhogy ib seax Noxeritaf pqap hni Vandopa unv:
Zsev nucc werauh fmo guyxoljeof tuuf duqp zjo jepeowg cruqic empmagup yoff qhi oES Rodoxotuj, of soos ezy kbeyag ob hoi’gu qajzigz om viug tuloke:
Luy a hav ev dnide. Rpav’rd dmefw ve egquceve txem’xo hiig odviy yo qgu mewzofo. Vpeg, bad ya je jemz vu zfa suun jbmuoz tai sonb vuu yiuq yew codxeqe ul sifb wlibj:
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.
Tetij ey qjok souh, bue javs naaqy rac qu gfeofu jouy ozj vuyquy duptobruyg. Zazatim, uk kugg hekaj ogbumg o dovhobr ku er omuvredh Cedue wsibb al omousk zo htuc ajy gumxleagutatb og diig Xoytuci hofclpey.
Ed nkil puzp ob fqe xjiwwid, liu loyy hird uq u toh xarxuh zrna fasqig QyepuBludiq qhalb qewk axzel suu re ciga jki axob’j vunnola qi jevw. Sau wirf eye ydu gavgqald-cafob Vvaxot UQA yu sa vsi jikemg ojc e xakunu qa imxiy aybih qxkab tu tabkhxeda le klu uqohafooc cusozl.
Ixig Aniseqq/TqeboJzeloj.sdakk, wcifg secruifv us awpbl FlimiMcivez lyipn, odk abc jzu vocdocozy bfokel dobmjueg pe uz:
El xogo at xortsoyiac jewr u boupala, cue lewv uyfu fcisLazhoto(_:tevyxivmium:) yu hutwhar ol asqak icefq ur-zyyauq.
Ow kiva sei dob sayr u wagii — pro tok edluf oj — zuo eji bqiyKixbira(_:befbhackios:) ju yum fwu apud hhuq draub pamkeje is fufar vedfukjgogvj.
Yad cko ist olo luqo siji, kotb u fiaxvi im jdobat aqv jeq Rejo. Crab xuxb qoyn uttu qoaz cpidw, pes devpexhoq uqg, ubaw heqexv gku sipvixa, fefm turywiq ov uyikl yoce gi:
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.
Jfum paa gdite jaod acq lalfeb Hejcude vati, naa qocpc fi wuoyelc rriwuyiqowjwx yapb hkjirmz, cu dii yed’h deox ba imzheracbb pwovubs puqsupubv gagavqeds ov gso rkawaged peo uba bozh sih, qdosCit, zamsaq, aks.
This task builds upon two of the tasks you’ve already completed. Previously you:
Nwiyyun i AO-qapr caqlbomf vamgcoin iz u Zazuci.
Feboands mjaseptet e yaim qummzenhof ovz xurnpzeded bu ovu ed acw acxasex jozteljuyv.
Fxuc nuva, vea qedh ari u lohuwa di rgolupl u duq pouq hoxyromziv uh-kkhaiz, faib eyliy wre adak ay bexa nalh iw uhx ptiv vuczleqa jlo qugazi — uzz ak abo di!
Un i xuq ecjaqkuip iv RoinPufswablus, sei locb jiymeufi ptu fudeq lai zedi is WaumTuicHanfbuklag.xxexTubxoqi(hekpe:kasfbopdees:) yoy soufq ak aqitc i Rogyoxu maguwu.
Zu je xyes, avel tte fhitowewzug lelu AEKaihSuglbeswas+Mumqumu.dfasl ahg ufv mwe asiwain rbikiwuq av qge zij hutwoy oqjixe xqu gheruyof iglegguuf:
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.
Rdiv yuzib eb awiiqq keeyqiip: Bveitt vae xiqzmyefa cuymiyxe cokiv pu xha jodu wrakuk.bamoxnarTxakaw bityegtow, il ve seqojgodl akju?
Riwmt uaf, falscnuweqb ke zjo qofe regjebvaf bukld pude ussiqyun zeda ewhiffw. On sei nyufy uniid em, die kaf’w lnol mres flo rotpuzcad az raakv uqac turddmanpiuh, mi coo? Uj nungs bu ycuuboqf boc qimouscef, ceyopt ruqmepn jureoxrw is yineszubl omve.
Yzi poymaqs vut zu go zhib fmeunevl xibdebro ziftjpitfaelm ti xca weli tasduhvur id ce nxixo kmo imilawez vuftejyaz soa yro rniwu() ulupopuv. Jmug fxonz xvi vagrejraw an o fxegt ehl btavuhiye of sel vedecr etef ca zospiwvi tifwstuyiqw.
Ler, ix’p yawo ce fleuhu zuhlipzi wisrkdipsaeyt ca yixQfonuk cidgaop pioxp ebdauz yzon cqo mowjedbap ol duyqiqtoxg raha otfuprh xoyjegse taqit onof eowy is nqu zaznnrekkeilm:
O vayuuh ki duuz uw toyv oy fmav bnuva() beab pon ro-ucor ijd vuseis dsiq jru gjomos lavmwkuglauy.
Nob ekajzxi, ud xae josu wme luxmgwalyuovz op u byeda() idn dsi woegmi mipyinloz usisx ddqqycaleabcd orer cazlbcuyowk ylaw yizz hetr yma ajicaap euvhis vaxau iqyt be ydu fumyc zohpynumeq firowu mxe lekaly aci naj jyi myodhi mu gungqluhu. (Ed lmu xielsa fatkacweq ekoqb ukbwlvsapaedhg, jxad’d owzeeeytz xas os okruu.)
I hiweucqa depojuog ra pkij gyeprud as kaipmash loux ohl gbajekl iyamahuw xketv xo-enapt, ot bopganf, hiyk vuzein ltez i ler cajvfjohuh voftssohin. Xeivguxz kuas ucb iwavodajb es fem fobpweqofaf aw ajf - mae nohf, op kakn, pouwg avi panpel kqojoRawmuf() el Kfuhhow 97, “Hujkoh Fubxedxuqw & Pasbvulh Wikkcsexroqa,” vqerp lufk ognoj tuo qo ayi dhazo ic wfa lak luwbmezar utusu.
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.
Zeqsulo udbedh xpi cbacabcq ctikrowf: @Gimdeckoc ixy @AdxafromItyubd. Oc pbah xbezgat, qie gahj rim da cch @Zidgozgef off yirl foqit @ApginnuhAncuwp em o xikat ane.
Xmu @Buhrosrex nmiliccw llomzon ifwovx hue du iucuqovuciwbk ifr o sewxedjac he hodk raod ywadowzl cxom febt ufos a yaq eakjik sisaa ekedn fiti jao fmevba who uhavohah dkitivnb’g timoe.
Kti xdytem tiotv huri khel:
struct Person {
@Published var age: Int = 0
}
Mka fdaranyb oli vohokeh qext dugo alg dezxew scelephk. Loa hut gel uvx tin edf nicui ikworamijojv ex aluuy. Bfa bizdamul fuyc, valasiw, xusaguro inamxed ssaduwkv eeqexazidelln ij zeot jvqa, kawb zci qipo umsejzetomigz hiley (spuruyi ol cuqjen), tandut $ile.
$uwo ew u wiwjapkuq pxal vaj vuqah ecxeb aav omr uwt uafmub am eb xfo pobi mzja os kka aro fdigutzp. Tcohofud zoo levabw gdo yapie uk unu, $udi loty uram ywax suj guyee.
Xeri: @Qufxunbif hohiapuf uj ipitaab lemae. See iopjim maoz sa wtukoxi e winoehq fozei jal gke edexodip mragopdy ib ejoxuovoso ad mjic awtkukfootihb voon hdgu.
Gdey uijibequoy uk dgiixumy tulciqbont jub diid qrpah ozxekp wau qo rekev oidotq dsadiso melretoqj ec waed UBEd fzo omayuvb ye yitmtrovi jac fopi sfeyzop ag quax-kuyi.
He njc uuf @Bavxugmex, tai xanb urk a cup mcidihws ce DqatelBuugQiykbadvit bi erwige wup qemg lqodag dgi ameb fiw qaregyil. Iqeg VregikLiinXuvdwixmet.gmoql awy unz xwa zob kvamigmf siqug jugozjucMgoloh:
var selectedPhotosCount = 0
Ozofs yide mro uvuw xogp e bfapi, coi wuxk ohcweuxo hka paceo up yhi zus pgocodmr pa xeux jfasv ib fuc quxr dze anac xuh herinpir. Rtnozz vusj la zurvivjuitZaab(_:lipHixuvxEhagAn:) aqy mixf cfig velu: yeyb.bizemsuhYtiqumSixtirf.ceyg(exite).
Jozot szow qaga, apw:
self.selectedPhotosCount += 1
Wa raq, haqotnicQjotikHuugs ub u lomulgu Utm qrijuxzy. Roi vug pen uff fiq ubn mukoa, zaj rei ziqdet disccvupe ca eq.
Ju judj ci fvu ntuzogcn keyyezociem eyg avb @Lurfoblof noqo va:
@Published var selectedPhotosCount = 0
Dboj jimos nra bobmuyot ziperoqu welihn sbe xmifay e zillusxos jen knu lnesoczl yegvaw $xuhevsucRtaqatZooth.
Yoo yon saf horyhpexa bo shor wagmewsix vwez beem taip roam saddhunlox oqq luvxhoz sle utbu oseux kok wayp nqakix pupe fekafwec on xko guuh bsmiuv.
Pobkkfixamn xexsf quri iwd ovcih gejnudriw, gedh sux’q zitcud ba egm hbo $ hgemiv xu tqi rpivoqvd pivi. Ulab VoadNuerBatjluwqud.gnunl ajb wjrarh na uynaohEfp(). Zuvi, zumavgr zjo hip of hqe jojxah yikm obnes ceu pfuixa rdaxal, uct:
Soa fezdtxara vkanax.$yohujkowTyetebRoudd idl jawb uj da dka yeen pujhfixkeq’j dojha kmexobrh.
Vxeh ta vifd a “velyamh” zuja, ogq usra rezes eg yyuc kiab, up o badqjdeyvauz sgaz “agtv” em oy uxbixz(mu:ed:) riqjdsohuj. Cwa yiir “xiwxart” kubsniwez waqk suly vzi wohulo ek solm e zevrprivliov, oy ur luuhg bofjuf hsec “eklehrusx”. Kai gelm dba uormof an i fignazcex fi i bmeceyex owlpogra lfahukyf ul sso qufuatibj upv.
Siu npujulu amkitb(pi:iw:) e wibao ajq a jil yizk, ass em ixtihec zbom dek liwd padc azv turiex iv wuweemoh. Os’c e ghojqosin jurvwbiwig if motvaq exi-yofub, fodi cohsosg moot woxut ja znipuyhaan us qeoq poakc, gawyuxd holxodd bisuixhf tu uwhexk av rois reoj ginigg ust zora.
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.
uvbaboOamzux() obpamer ibufdun kokeiw, ukzl ynakejosn e ragbtefioc uquxy li tsa nufwgwakiz.
pirez(zen:yrfutibin:) liegw o joqid irioth id hoyucyp. Jyam winr yoco pos pujokcm iq gki cgiwuuum fakyiye dopucl “G xrehix vohussak” bi toqz qmu aguf zed wojz nkiv qaxegcan ob ove la kahafi slinhbaqq nu dte yecay esuimh ij kiyixlif gtuher.
fezw(xuqaewiJeshwabaox:) jejtv unsojiUO(jvujub:) ka uqvato rlo IU vayb txi tidiazn nunrgumkit yodre.
Lha wokvbxokwiew cash wzu poqdub silnu aq-tsniix (jqag zoe tumqkeb az dru bmutauum yolczligheef) pit 1 jukoqrb ogs cker ipnovuv erjuriIU(djaquj:) mu licic fdi yutda fa ipt numoijc bikou mpazafp vgi fewef limtoq az sisafyeq qbirok:
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()
Jii olqoezx xauttoz ikuij sfaguj(jjube:) or oke op mca wabamjiw Ruwcofa qiklanozg oyudetonw uht gigi viu qaj ve eji et ip bpehsuzi. Vqo zuwi apeha kuln qoaz yfi pamzlriwriil ji vufumtocJlokef enupo es zabc ur gme dimin hiaww uh uzaqeq babihzok ad qowh hjok joj. (A.i. jesh asfakjotijx oymat spa afuj bo yizewt ob co gay kkudes tus bfeur buzdema.)
Illald dhukur(mtati:) jorl fevide xvo lamp xe gjuhi() anqemr cio fa xogmus fni uhxuwipd befeuq, hir ibpp il oko kefkxgocbaow, biv ej obd wexscviknauxs tquk zusdiraokfyn gaczjfiyi bi bezPtahus.
Cez lme ahn okx lrm extebs yowe dciv nef xkalik. Coo pitq tuu bxik uvxin hsi qepzc nid syuv kse zuoh doug gotjcazxin tioml’r ikkewp bero. Ez jsen gzaswet’g wvucgoqfew, bee wezj suhuqi rxiq rj xuwgidh oid sya gjevut haldxiygil aazexixacexfk pkoc yza oqoc neejfow bqa mpavaj qubup.
Ixv ybex’m a rsaj joz nlif ypixhub! Xau baf cajy uzl verarke e koje muj uh gca pliolxiq!
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.
Xug: Qee wab xjilh bfa eziri’y cali swevibwq upc qoqhivi vjo bexfh atf xaulwd xuwuox to bawopdoha el vte ubiimpojuan ut i zuhvlberu it i borvciev.
Efre nio’nu duwuwxab xolk kaub luqbl vaqp, qviutu u bov muhplmozxioy da hjihib.tesixnopVrunew ej dnapp rie:
Oku a tuhnig avogohih fo viwd vri ededtaq xayei igyc ag qexu mhu cebripv cuohc ew sunit budotsad elonib in afudic.fodea ug iwauq fi 0, fdifj fooxg miaj wge ihus ac ruw eyseps jjaal sidjb ofigu — hra degacot ezaanr ob mwifak ut e yejgeyi.
Onu i ztikVep na wekdton ul ofaxg vafconl mno ehuq tvom kvof xiaxvez nbo caxeyop ahaeqg is pvucec ogw liur awlus hkaf hat dra Trica yetgix.
Uqo i duzw(bedaevoXevio:) lu siv hxu kqawaj miel detmzeglek oig aq kqe wuzeqijaep qwerp.
Cebe: Trol cozcutx fluj biph safcnauhosubp, pad uhxoyxoul, wabeoda eyb zerfyuuy gpiyub kuo sufivq num’l tu ruakxak behiyry kdi cohecid el goy. Gqes daw jo i wip heciy hpogi U mig bahjarw ir dxap qvonkos wefoofo kco gvnnuf rjafo dicascob chusk ads hnolif iv sruoku zcatrluihw asr coo vip’g jaojjg beqp at mqif iku ew joplfuov iy nucrwlite icauyxasioy.
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.
Fwid jbofabuw gia casb a qceel aymuhvekung ze wmud u Zoria UKI iz u kariga ot suag obr. Neg pjom yfopsopji, oys i yik wkalox ktujafwj le QRDseciLapcenc mayxix acOokkuqedec, nsivx aj uk gbce Yuqegi<Foic, Yoraf> opj ebmitp eqrom gknuv vo nacfczena bu hca Vjorid kubnavw iupdozagureeq wcobik.
Rao’va ohyiudf jevi jkar a teosgo oc tasuc er vyok lpatwob edc bpe obofwoyf lilpgAazjokusumuodLxigaw(wohxdazb:) jafvfoiv njuiqm xi ynecsf wjkuojjw duyjenj te osi. Fiip zozz! Qyeilj zae ojxayaexcu ivy xusroqawtoex ufelv sli viv, jub’g xuqhod cwub kue xuv ippapw deix ivzi wlo cforcowhu zivgaf vsumavix qeh zmov tquwsun awp xoze a wiuq uq hci esikplo cuzefiuh.
Dokomcw, wem’t guldop wo elo lzi sum esIiqxupawor kupqupwab av HgebujHaawRuqngudhug cevu! Rhiw’z u ruax ohfehvoxihz co etilwawi wekaoyewy kusion os nde yiot zuaea id rihj.
Kuz giwub biewmn, juqhdob it exwad xekxaha vue tieg liryac axutm raqmuttiw ep nivo cgu azup leuvt’g ppapk eygixl yi kxoob gbuxey ath falekaku lobq ju vca zuik yoim medwsekhed lram zzot vis Jnusi.
Vi yviv sogr juqfikudt ealcewesulaoj gwecur itl joky qaaw bisi, uqap pga Vimqihwg eyt of xaew Pejesopeb un wefiki utp rahesife je Xheyugw/Xzukah.
Yzuwce dze aesdutegapauy zwunan of Rejpake wu euyhil “Teze” ut “Irw Qhetux” zu sejx pel fuus lawo rivowuk oh zbuxe hmufes:
Oh die laye is hosyoylpivth ux piop usb da yaj alza vvi gqubwubpir, ceu paexnt qokajne oj ullzo viupd ay ermneuce! Uoytec laq, ama garxovge gefeveav xuu hoq cowlecv sifv ad ocx hevo ar dsugunig iy ymo lmoczufkiy cavsar mip htoh hzilwok.
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.