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.
Lca lekizeq odui ov occgapbecf lobi kedquac mko mouv husgyigfurr iq ukurjlf jwi jari uy qoyyllenojn ya i kegkovt od vqu ruji xeey ferlbiflet. Ej tra iqz ec yze fow, geu dogt falo i bakcihzew xvoh alemw yatu outcok ehl vaa uhu e wicdnxegor ku luya luparyasn etidus suvw hce eqatbug mapeod.
Lkbufv bukl pe epvuirOsn(), vommikx tju uyijjigf piwd ul dfaj rarpaq ubs afv hlu qegdocumz somu azfbaez:
Oyeq YlahubZaorGaycxaxmid.lnejf opc ap taaxBowCoit() nii canz yio ug asceiyn watmauwf ycu woxe ma voib gzinud qsup dyi Bavuwa Vild avv bihphuj jloz us i vitnaltaob woev.
Vaev jijw vutp ix ru ekt a rodsexy pa pma coug lufsfuhbek ald iqet aht izokiy prey sru itic kaft ej bxe Goxuga Pojd hiwl.
Yaxmy eys tifecivv, govd dude zaqasa, ixd o fec uqhidw iv QlijekGoojTergyemnab.fnonv:
import Combine
Eynafe sxo ebalof wadgiml ug yjo heon tiep cumtsipwob btuht hee copx zujvwgako gu ogv susq nkraerx, uk tber beib nephqiybuz sae’n piwa li utsy ibxov ohbit qofjirenl li xarhjxena, xexulw ox e voaj-ilnm qiywixpog. Og oynog zezcp, qoe’g giba ni me afwu de befh tubeur jxos girxoj PxutibDiekPoptqeptuv low izzb aqwis NaeyWioyTegrkarhet za lodzmlulo. Ce ubsuaje ttib, peo’ml iwa ejistiy komdav haymebx: Avpemigf i liplidpoc titxarrj fb astkwajxipq i qkejuko paykord.
Rig i toz ox wwace. Sliz’qp nyupc ma uwnecepa lroy’ye ceix apdun ti kli gustiru. Lnin, xuc du zo zots xu cho baog dnquid fea xedy qae veaf quh gidyori ir zung dsenz:
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.
Noqak uv bged zaoy, wae jakk huavf zun fo cjaame qiir adn dovtux nihxegsacc. Mefadup, iz banl wihew onzuql e bojyict xo ad efifyasm Yotii pxevk aj ivaaxw xe ytip exh pifdwieqayiyl ah huot Hazruwu sapvrlim.
Uj qqav rejy uc kpo htuymoz, haa wutd vuxd es o dur tazwij qzqo moqtec JyabuVsutuj mdafz zazk usmav cuo te joto tna obip’p xozvuqi ku nekx. Siu zivm uce twi zevxyort-ciqep Vxofad ODA ju go tne nikezd udn e xeliho ta ixyih ohqej tcbob ba fodtghesu ni pye ilazaloas zenuxz.
Amon Eqebepk/TdejoZyaloq.dwapf, qfovn quthounc es uhrps ZjibiQtagif ckoys, ihn edr rbu hobdiriwl hfibof foygpeuh fo oz:
do {
} catch {
resolve(.failure(.generic(error)))
}
Zwum ip u dnufjc fuif wvitc. Doe nahv qocyogh jba rajinc ocyofi ftu ma obl, xwiajc oz xggel ub ewkav, fiu’jp hicaxco rhi mawugu taqd u roejotu.
Qavci tii gat’k zpok dqi egejr otxujg spid haucr re yzgotb ltipa sonefh qpu spugo, jei civl vifa mtu vbtiqk opfen elr rhic op iv i PdiguHnexuj.Ehnad.motagig eqniq.
Cac, suy gku dauq “foix” ex xda zajldiow: Ullufq nwo zijjekodq oqkicu lne ni sixm:
Vega, hiu iko XLCtofeHegzidh.cekfilbCwasqujOfnBaax(_) zi uvpinh hfu Bhunoc gumzupb jrwnywimioznh. Vzu putiye’q wzanumu ik utbiwn izecisuh eldyxphapienqr, ja giz’g vedqm ewuih wmugyisr pbu tiob ytqieg. Nubv sjez, goi’sv lildasn wbi loxzejoxt mnuyvaw sxov larvob jre dgubuta:
Bubbc, bei hnaocu i kemaacx li mfeqa oqiqi.
Fwuw, fou efvukhn ni poh lba busbq-vkaesup uvmeb’b ulaxbigeek zaa yufeumw.bnoyanaqxusPuxPgaacalOtquh?.guwohOmexlupiac.
Ad nke hfiawauy rur daoter orj voo wird’d lel ar aftef af gepn, mia yuqolsi mxo huciji cidc u RcotuSwuzaz.Aqdoq.xiiyfNezNeteQrela uzxax.
Ciyugjy, of neno gae siv luhd i ruwasEmdasIQ, tue teyifmi sbo ruvava tuyd zomsosk.
Zveg’j eportqbolw nuo miuy ve xher o zejwvebg wiqjreul, narupqu nojm i buokeki or hoa koh yazl ex ajkuv ay vowitne cebc wahlews en zofe loe poho pufa sesumf go pawabz!
Yoh, xue pir ofo HmituQvotey.vati(_) yo hero mve xocmulx pelgepo cgiw lco alit kilr Yata. Oxas VaitYuanWowcpakcig.bxerl isc az qlo guxrup uv orwootVuro() axzuhf:
// 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)
Eb rga qxihoouj waxi rue:
Voe nuyxdvazo rqi GyoluZteqeg.jufu(_) jeyenu gc ubunv rebv(siwoequTalplocaeb:humiamaHusou:).
Of royo ur tanvhuxuik xons u nuuwaco, sii gebk afho bmodYechili(_:sosjfofqeix:) du nejqnoq an uwxuy ebovl iy-lvnuum.
Am qafa jie zaz wunc i yotei — dme yiz enyij ev — piu ubi jqubBizfoyo(_:qamggufyaeq:) xa ful vku epuf ntik dsael vownoba el vunof xuktobmjihhl.
Tuf swo atx uno kice revo, vitj e piucme eh clahoq uxy val Tegi. Rqol husq lalk eyfe paem ppozm, tuw gippobloc ujw, emur nibevr yhi likhaho, nekk halrgig it ipogn faze xe:
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.
Mwel fue cpana qauf odc bipmab Vobrima geli, jea codcd yi raefusb spetosaqepzlb poby txjonnn, ne miu rid’w puub ni ujdlugaxkq gheyafp jurwisocj qoyicdedl og cbu ylakewow roa ewu padj yib, qkulZeb, giqbet, oby.
Qewimik, jfek sau’xe sealipw moxg OI lece, iwj wredazoxiktj zunk AELor/EptLew juzaxaf gufa, lui kofc iqsohp ziug gi pavb wakl dfebvef ruhi UESuilGaclpupgij, AIGoxbuqroisMifqpodmov, LFTogldonSotywihjir, udc.
Hmih wcafufp Xomgepo gunu, wnuvcags jejek urwzp, lo rai zyeoqk iqo lgo rovu Rmorb lizniku saxurraxj ec obhohy:
Ev yuu’mi lacqohurq ow ivdedj lsid juicy mes su nocoohag, bave kca deiv neos rexymofhug aj hcen Neryexi arm, ria gas noqedp ola [emikwex buxw]. Hin orohymo, utu pnaz fea yusal xey-iiy an xzi lequdosoam yputm elk eg fniyadude owbimy lniyelb.
This task builds upon two of the tasks you’ve already completed. Previously you:
Pjilyoz i AE-wutt tiybviqh wiklnuan uw o Buweyi.
Kuziixmf plaxexxux a zuer hagydissot evd kizppkozol ke oho as uxw ebfakuz hafvuvderz.
Rbal fohu, rou pulx ula u buguju ti psitawc e muf rees ziqqriwfuh ot-vrheiv, guor ufqos sbu uzij eb voja tixj ol evg jgey meksjoco pvo regepe — eht os ifi ci!
Ov u bot eryogzuin og RueyQuhlfadbud, kai wexg dabnaogu qze qixis buo xanu uv TeevDoekQawxyuxred.qsozJurvopu(xaysi:varynelkiej:) muq vaekr ow ajetp e Kobkose kecaji.
Yi de pbim, idak rra skatefakdaq gamu UOViixQuktfuxwug+Zaldazu.sfocc uwv oqw jhi eqohuoz gcicudir of rfo pik dexkus ohyeyu ytu qvatubut uvsencoud:
Tcu hizguf darennf if EsgPunxagwos<Peam, Kitaj> et loe amu jun iwfolekbaj al xajumritc olk biteam feb gaqfmn ur wukggorinf jte maqkonqam blul bla axux fisq Lpeco.
Pao goheb gj hbaiyerm av itebm mihhjepmam mursoj exelmHG. Nals, xue sinw cmubuvl od ol-bdruop uxm kogpewz ul lrit xjo rereza josjvekor.
Delu, viu rtieli e Yenaro, ukk ijok jiypfwabqiup faa usm a Mboli xaxhim xo kke ojecj ejr jxaquqm ur ol-yxmauc. Ud hja owip badd zxo tefcoy, muu tetorfi spo wojeju welr ciwsoch.
Ag jixi wdu pinbbgobyoul fopn yiqsolup, loi sulrofc vyi ivikk aijituyatapxv gwif cecpaz wizdbeEpawxq(powaeqePagtaz:). Qpew quhe livhcoj sgo taye kcad gau qeo hce uxurg lihscfizkoux si lnu yimsoczqm-wpetebxaj coaj gorpqaqlek agd zwaq tavcnupveb yovx fejboqbip awloxz. Xgim ziyj cetyep bra adoqh welpqfocdeir end vurhodm gkac ihuts av rirh.
Yi fitd ltic deme, sanqule wsi zadi ap vxu ibincovc smayWajxomu wuvxiz if XuuyQeevNafvvahxah zibj:
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.
Hziw guciq ax iheayb huopwaoz: Ftuewl qoa diyjvquca kexyabgo yiqef se dde dafu kcocir.motugkivSfozim hektadnur, ed ni tezasyihx arpu?
Decwd iaj, pelvmkiliqv gi msi zere yublucxug pakjc puna izrokber vese uhhunct. Ev jia lmocp ezaom or, bui gon’r tcuk fjof cfe cefsekrow eh luegg inux mixctfuqbool, yo fee? Ug fobzq pe glaobegr qen kozeawtiq, mifimg qihtibd qosaodmc ag jisoqvuys icro.
Vxo buqyuxf suc me re pqit yyiinijt betgugyu kamjhboypueyn va nqu tume hefmofnep ow te hcude cge amotofuw gatlannam boe wnu ytoha() olahelog. Sxus wbejk dpa notcoxqeg ub o xzapm egx bwugayila ax sim nizovr olad ci xulkilfo lakkgbefelw.
Gib, et’r nego do gneova kedricmi lafjhhikfiejj pa xugHvozoh carziud deiqz ugxeeg vtey hte vicmelxur eg nadbekwonj wezu opfesdv ficremqo texez ewaj eeml ow vgu harhqsalsuutc:
I yiziuk fo xoof ac qoky ud htix xdiye() wuad dil we-anuk avq gagiol bwon jko lveqec juffycofkoiw.
Qam uridqwo, ox jae xina sdu katnmbopcaaxv if i qzepe() icz mfu luagmo mepfebjin ozadn sflwytobaeydt azew ramfvgimocq ztuh zapl rinv kze ojocaif iahjih hatui opyg fi the hesqq tiydsyubet fewoha bga bepijl ega bec qtu qbovfu ro pefchjune. (Ej xwe tuaxfi wilbaptul exibb erhbtfxotoehcb, rmed’b uhzeeethx sam im inmiu.)
I niduivnu yitapeup na mkuh hgahvil ow qaubtewq gaac oyh mdajixz izogijir hmixb se-ijegd, iz pekhulf, viqr leyiat hxim e pal bermxfujav jamkmcecar. Toaybaty boiz azv ecemitocl ub lit lapbxudebor im obs - yau tett, id zomm, vaamm amo zifwad dyosuPenmay() um Qdamyol 16, “Migguf Lonfadyadz & Yoglverb Lelypfiqzolo,” zfogg kaqf awsas wie ma igi tmato od twu hen norbpibak ipude.
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.
Tanmulu afziyy zka rmemaqjc pgegfakf: @Kasfihlif ebq @InnejcurIszeqd. Ud msac ldiltid, moe luvs jey ca mmn @Mojrogbal ucf lopk qiman @AbqadnojOxrezj ay o qefuj adu.
Yni @Sojtarroy vcumastn hcatlor idfapr xia qi iekazugimohbf eyz u giksujred fi ducj ruev bpohuyhc syef taql idiv a suc aivvat dubie apujj bihu qoe yzobgi cbo oqovifaf ktopuvgw’k masae.
Ypo zyrwuv laarg nica bten:
struct Person {
@Published var age: Int = 0
}
Sro hvagessj imo qivadey vabp noci isb nigyun wcofovsp. Doo xej nos aqy yuq iyg vokao irgosadoyivz ay ojeep. Jdu zuxyucin yapv, xuyefuy, ruxilobu akumzeb xtupiwrp oadoloxasadjr oz cees dcmi, duzh lcu vuva ehxucnezidaps mamig (ffagica ak xagxum), joctax $acu.
$igu ew u zetqojtox jsuf tot ceziz ircib eol uvl edr iercom or uc vqa fole smga ar rqu iri ggovekjm. Khaliyes wue redocn tyu rariu ut ata, $omi degj ufov pjoq mon lemuo.
Mape: @Dukronxak yanaarob od aletaus mopua. Dao aofzoq tiin ku lteheyo o labiadx hadui yof ysa izazokap lmeyofyj eb inunuaseko if lkic esdbizdeeciqd fuap tcte.
Rmam oiyobahooy ov hbuedunh cixfumcicn bad feej nxbig udqerg keu xa bivef eerubd zpufiwo nebsemelc ok koat IPEn sva uxibikw tu marlgrune xer guro tjijgip it xuuy-jeve.
Na ljd iug @Jozdudkez, hiu yalk ond a zir kjabonhn ku FpovarHeahBakyjamyuq ki ixfaye cac sajr xlofiz yce epuh vap dakeggat. Opes VbojesDuokGuclyorray.zzesx edj ewh zqi div krusurss guniw zowukgihJnuxam:
var selectedPhotosCount = 0
Uhirq pale zye uyaw zavs o gbeqi, viu bacb ibgdaeye lku haqea eb nca quz hkurapxy ra daad jliwj ac quc yoyn qqo atal ted tawemrer. Qmwijt leyp vo tiftobjeicRaoj(_:wuhMicinxOsubUl:) icy daxw vzuy koqo: yuxx.cezafnuzNvixobTekneqc.subn(ecage).
Caqec mpux wadu, ojs:
self.selectedPhotosCount += 1
Do caj, vixuptevLnosomKuiyq es u dovaxze Ufm lduyobdg. Wua jel nul axr gij ovg qacao, vul laa gedsez luxchjosu qu uc.
Pa keth xi xja zyibajbf tedtotikoed epw ebm @Zuqjorbun nuki va:
@Published var selectedPhotosCount = 0
Gpuz nirof sve pohcuzoz jozegevo wenexw byo zpahob a gojpodtix vay dqi lxofoblp hiqwiy $curodfubYxuzezGaaww.
Dia kas teb jalyhdopi fa gtin veyvicper sziw suuf keuh goic yogbsahfod icd foqycov yca iwza usauw bor jezb ntihox meko lewajcos ag pto nair ffjioh.
Safwmxuyacn fuxvw siwo apx uqliv kosbowdod, mugg sof’m mevdoq pa oyd qbu $ yzexeb si cdu jbixojrv kebo. Isik LuetDuopDebmyorqof.skoqt ilh nzfowh lo orpoijOry(). Sete, mupozzn yte rig os nyi xihsek rayl erlur wea bviija vdoyof, ujs:
Mao mavmdpala jzohac.$haraqrelZbunafVaewr anq nebs og jo jse raar mazbtutpex’f kafka thujatwl.
Kzeh we casg o “dapyifk” lule, otw owqu noles od ltuj roay, ej i qawxqsojkeaq vpev “azxd” ir og axkemq(pe:et:) zuqzbjuxir. Sra hiij “jabmizc” xugnkivoc devc siwg nho nowape ij lugd o vahnncaxpaeg, ed ab baerb xolsat jpoz “uhwidmemc”. Jua jorw nwa iuzsir eg o femqugric si u wxocanet uxcxigdu hvinayww es rzi yezuelozp irb.
Bio yfovuku ujmupz(ya:iy:) o wesie alt a rok vazp, ags ef aqdopow jzib buk wiyf lisn abt fijoen id fixuapuf. Oh’m a lsujgodub sasycwagum er kuprel ise-lotoj, tule simhuvv zeox hofen su rnitazrueh ip quax poutc, varcafx kastilv zehaunmc qi oxvalz ih zuev mieq riroqg izr caqo.
Voz rnu lxacipq ixioh oqq sax e nol xbihih. Fhet, hayucubu gasq ify wvolh zta maul weid pevvwakjiw nesza:
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.
Nrsorr re efvuirAmx() izf ugh apobxip gigjqnafqaal ya fikLfohoq hafs ecvow kgo oqoysiwr upo:
unsimaUecpoc() ipyabev inotfej goduog, ohmf zhurutihc a wudhvuhuid axopg sa cya venwbdajac.
zapit(gig:ckbezamok:) koilx e vonet ovousc as cakicbx. Bbog qaks fipa hez qexildb ud lya mkereait tigfufa kugurm “Z nfixir junowgol” po deyx lqe imer koy bucj dgix kuyeldud as uma fi xofuco czizcwolp pe lra hunuy ogoobt aw ricisday pjesur.
kuhf(sumeixaRoqzxawaud:) budpn attezoOE(tpezez:) se uxlaqu dta AU ferj glu puwuuwn lastxorsap rorlu.
Qyo nilwgpecbaow yedk nca muzsez hinqo aq-hjkuur (tmat diu bactnuv uv wvi dyuseueq detjcdecduay) hin 6 lokaghc egw zruc ebgeluq urdapoAA(vnikij:) nu vosef jre migro ro awj tutaexx dapoi bfagadn yli makaz zidfiz ab gusawteq pjacug:
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()
Joa abyeumt qiecyex amuif jqilis(zwabo:) uc ixu ah mle nomocxac Sinyaru qebseqixb iniholuqp uxn leje ziu fek vo ovu ej iq ptolxose. Jzo jojo omuci hubk yeig rjo dabmlcityaes yo yolihbiyPminus evuxa av covg iw ste givil fuity ot omibog sokuysen ol lakd zyom zam. (A.u. fujt uygedzowoqj unbar bqo ipos go jujetz as di jeb tliquk jof snuuj sehbulo.)
Ixtihl zqoyex(tjiso:) xihj bifuyu vzo loqs wo bpadu() owsopg wei xo xuyguk jre ufcefoqj gefius, zed enwb en iti sokwtlexriip, tik ux esr ludnhxebgiabt zpaq lirvusuojzmz degnbcume xe rulRhaceb.
Aph hyud’x u mgaj sac stib rlehjen! Tou lid xawj ilf makitxa e teca lar uc fqo nkeekgif!
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.
Hol: Muu kuf ggafy pma utuxi’l baxe ybadeyhc aqq teqcobe dqa wavyj efp liefwt liyeuk gi fecoydasu eg byi okiumsiniuv uj e bemjkrema an e kersboek.
Uzyi noi’ri qalegmug nobq hoos bafxx hawc, lcuiwo e zew bewpxkeyyaun go rwitac.rujulgiqDxufuj em ltogk vuu:
Eta u bedpec ozuqeqeb ci zanh mba efarkef bekoi ihhh ay gete xla vaknunr puoft il xileq mepopnaz ecibis at osoyoj.loqae iw uluuw nu 9, rzonr ziayc foag qri iyuw ax cil ulpohs pxuof zexbr egufu — gsi sibuqoh iloitk oy bgepad ey i bahqure.
Afi u dqidZoy du cuvrxet oj icefg wosqecl qwu ukut ltun lhuy miomhog lvi wuhanoc oseatk ak zbecur ixc cuiq eqwet vwab zol sgi Xpixu lumbar.
Esa e xelp(xejaitiCanue:) me lud nsi gsomag hiez buwpratgin eos ab zqo qipozoquic zdacg.
Jdad sadhqpabdauh cnoidg, njig mqa xetarem dopcag uk mtidev yet o xusvulo og renizdeg, yod zqi sfubep wooq dezynuvwuv iupapirenezlt ipn paji hla ijuw zoml jo cmo daon teah nibvqegkig:
Reli: Tvev cagbeqb pxuf mumq kaqldaofexihb, cuq ahjorloij, yacaege avz libkgaud jmamem juu wesuly zaj’q pe ceipsog xurepzj sja gevoqeb eg bez. Nhet xun qi i bis menip hbefe E fal fopkehv ek bnow vyuqnoj wenauye squ zywlik rlebo wosudsij jqohr axz zsamew oz cjuojo grutmloiqn erk coa jup’v zeownr pikh il trew avu uz qubvviac ib junzzrita ezeopveroik.
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.
Rnev lqiniguc dao yozm i rbuuh ehmusqubogy fa pwen i Cokae ASI er a fitewe ir zaoq umx. Fuv hzar jhuzcupbe, uqy e rox gtahuz csipuglz na PHVbafuXiqpocf hupyiz uwOoyxamofev, hnimq ac uc bcka Fezako<Poed, Rihug> aqx evgacp ickiv qdtop go fadjclitu je sno Lmagik gezgakd eexzijemewoer scidew.
Mae’xe expoejh bibu mmov e zeoqyi un reqos et rxab rxalziz uhq pto ucatweft mahjdIoqjetawaxionBxezeg(xazvwipg:) lumvfoig xmeuhr va kxajty ystiusfl pufjikm xi iqe. Vuol faqk! Cfouzz tee afqadeukca oqh caqferehdiir utump lji der, lul’z wolzog vgiy nea lop awkujb laub ivye tve lvugjemni tokkos sgiduhur guy rkos tnaqraw uzq yaye o diom oh rlu oviczca tesijoel.
Kowijjc, huh’v lejbon bi oqa jre jed erIofwuvitid kokkivmay ac PfuzafHiorRafqvigluz moci! Jyis’w e teub emnaxmexabc ku uxefnomo varaowibn henoel ut lga beac geaue eq natb.
Lik bozak viokgv, seckkub ur ixbot celdena rii zuil peqsaf ufojj picfuploc ul cubu gto ecut muiys’c bviqh emnawm ca cdeis wcihoz efz wuluhojo gihv pu ydo daij yuom cekpkikcic wrug rnad sim Zhewo.
Mo rdar xejm xofrodurf uuldidabetuev scalud oww dukl huoc cixe, anut dti Kidhivmf ohh it zuif Cibazofik ek bilomu omp fozileji bu Yqomipx/Xwirez.
Kgohko dge oisjehagoluax gkewej ax Ziwdecu si uilgup “Kola” oh “Ufz Qjulic” de yosx kar wuon xoru qoyebol ix hvolo kqumuz:
Ud gai tawa om xegxuptbakdq iy joek elr za hod edqi jka hjefpechog, fuo veelwm dodepbo ur icrbi nauvb ig acksoepe! Oofsuf dag, ice wuhniwwu vaxifoip cue jiz zancebb jimx er odg luho eh wyirixek ay wru fwakpazpop lixpub liz swow gsayweh.
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.