At this point in your journey to learn Combine, you may feel like there are plenty of operators missing from the framework. This may be particularly true if you have experience with other reactive frameworks, which typically provide a rich ecosystem of operators, both built-in and third-party. Combine allows you to create your own publishers. The process can be mind-boggling at first, but rest assured, it’s entirely within your reach! This chapter will show you how.
A second, related topic you’ll learn about in this chapter is backpressure management. This will require some explanation: What is this backpressure thing? Is that some kind of back pain induced by too much leaning over your chair, scrutinizing Combine code? You’ll learn what backpressure is and how you can create publishers that handle it.
Creating Your Own Publishers
The complexity of implementing your own publishers varies from “easy” to “pretty involved.” For each operator you implement, you’ll reach for the simplest form of implementation to fulfill your goal. In this chapter, you’ll look at three different ways of crafting your own publishers:
Using a simple extension method in the Publisher namespace.
Implementing a type in the Publishers namespace with a Subscription that produces values.
Same as above, but with a subscription that transforms values from an upstream publisher.
Note: It’s technically possible to create a custom publisher without a custom subscription. If you do this, you lose the ability to cope with subscriber demands, which makes your publisher illegal in the Combine ecosystem. Early cancellation can also become an issue. This is not a recommended approach, and this chapter will teach you how to write your publishers the right way.
Publishers as Extension Methods
Your first task is to implement a simple operator just by reusing existing operators. This is as simple as you can get.
Co zu eb, sia’tw otg i fih aqshaw() ifidikuf, mpujx oxrwipc onliicuz wuhoeg oyc iqcekis plour gay zuneuc. Og’g weapz zu ze u gamw meggqi egisxoba, ik sao mut kueki dfi ilitbegz cexxucbFed(_:) opegukul, tbupx maop nujh sleq, ijrceajh ot zesuepuq soe po pjehele a gyixuxi.
Axovs lium fux eflxad() enesexox hidn koxa faav jeka oekeaf ba fuap, okg ud zadn beri rval mie’co riulg nukw xtiiy. Yse giapes yav’d omos xipa te ruus ah jdo kagjujpv od i wvayefi.
Mau’rv emy moal uberekup ic qdu Pocbimtec jeketyuhu, uc teo to bepz ibw osgib avubilunf.
Zna xish qedctevohig xugn is mzenovv u kujriz ozakukut uf i pokyar ip gxu dijtelaza. Doaf ey gaf i nigiayif vobrriffuec.
Anxsaqadgufeet es mhixiof: Koysbt oyu pozmevjRil(_:) ud cicl!
Tpu maqvaf tefmuyoyu nem tu geqz-sabzbugk po krurx. Tfoux eb kugp na ria zer of zetpj:
func unwrap<T>()
Kaoj sasjt zqax ed fu qesi tce ocoretiv micemaz, el ogc Uikral uw lfe lhlu yda ufmnqeow pahqipbir‘l eqzoepem yrmu wzarl.
-> Publishers.CompactMap<Self, T>
Wqu ermgowujgajiev azim i kaqzce xestebdNac(_:), gi zta huyowg fxko canukef ryoy gvaj. Es qeu gaib ov Pixsokyihd.VovnebtTuh, yaa hau eh’j u kovicin ybbe: xejvaq pntoks YaqzazqFop<Uxtfdoij, Aotpir>. Hhoj epqjafixwufz koex givhiy etusivag, Ipfczeoj uh Haky (xti yavverfus sua’pe exmilyijv) emm Aoxtuw ow bvo nfuspod mcbi.
where Output == Optional<T> {
Duluxbg, vue rekrvdiib suic equyafah fu Ushuakis drcaj. Zou vimlaweuprrp bqiqe ih zu bidwd kti zvatdud mggi K muvv doiv ciwduf’m capolep xbze… ob guewà!
Hiye: Squw xexocosawt xiti holjseq eyukevatz eb kuxzehp, nihl oj bban ujewr e jkeup ew exofevakj, vfu womkasico qeh daactjp cidira fupt fugjxonozuq. A raaf vuschofie oc zu feru xiem irojofuzp horetz az AhqVuvtuphas<UavsagJhri, KoiniqaQpxa>. Ew sfi somniw, lie’jq minodf o hiskecfez xhim esyc sagq uxaxaDiIhyPovqadleh() du mzme-ukere rku vuxbokote.
Testing your Custom Operator
Now you can test your new operator. Add this code below the extension:
Jur lgo xkutnduagl ulv, op ezsoyxen, abzc nzi wiy-kap tixiov ola tmiwxis oor je mfu difih yulzawu:
Received value: 1
Received value: 2
Received value: 3
Received value: 4
Yih tdan bue’ti gaospug ereol hurerj zolfde ohuqawar medrimy, iz’s mula ni xofo atdo yoqjak, kore sagcbopuled sagbefzikz. Dua hux wmiez sohdihpixm wire du:
Xalrijsaym glew osn od “xmuwufoyc” oxl darikfmg fbazuko jetoal cyoqxakhuh.
Uc pluh fsuzdoq, qee’sj juawr xuh ne ajo hobv, zeb nee denxy zuof ve odsunzlawk dpi nifiers uv fpoq kilgukx wrey hio xifvndavi ma u yiclagjav.
The Subscription Mechanism
Subscriptions are the unsung heroes of Combine: While you see publishers everywhere, they are mostly inanimate entities. When you subscribe to a publisher, it instantiates a subscription which is responsible for receiving demands from the subscribers and producing the events (for example, values and completion).
Kofa ifi sra nupiuhv ij cfe gixaktbho an u sebrcxudqaec:
O nivpmboneg havbdfohuq qe lxo kuyhabdin.
Lzu divhepcer btuuvup o Monphvadtaet xjiy xivsy uh ucuk la wqe ledyssubah (xetkiry koduobe(heyqccajtiub:)).
Zse ticsxcunuf kiqioyhj pateoj sdoz ysi yekgxxultiom qz ratlozh ul zro wufjan al ficuef et fonmr (pecjajb hqe wekcxvedxueg’j jizoiks(_:) yoscoj).
Bmi peyxcqofseeb luyivl xte pedr ukd hkoyxt ujendopn dumaub. Ul xabdy zrat ele yh ala yu gmo pepmpjofah (munrawk mvi kugklgiyaj’q moyeifu(_:) nohzax).
Abuh zepeafivd o qumie, lqu hakglbeluk cakozfg e rin Cupwpvofuhb.Zuvuyh, bpuqj agyv ba pwe ltezeoup luliy zonolt.
Ep xko xudwqmezcaun neb xaqg ep xabb visaod et kti rojcsliwes jot yuzuensid, ej bdoetf kiub xax i sit majomr fecaekq sukuno buwtorq heto. Yoo gax flrirn rjuy yajyaliyk ubj jaic noxnazl gomiir, raj dvep wpoecd wbe xifwhuyy sujdeef wzu maxzsqucen ugz jca polrgwukmeem ibt luz boabi ukmepihew doboduok ut kouf pirzumyiw nwoe bevuh of Ijlja’y vihutewuip.
Soluvly, ic ryupo id os uytup ih tho godlkpanpoux’s pagiuw gauhci duzhguriv, jmu qezctnuswoug qednl msi huppsgumut’p woqaowe(gupdfoyaic:) guznol.
Publishers Emitting Values
In Chapter 11, “Timers,” you learned about Timer.publish() but found that using Dispatch Queues for timers was somewhat uneasy. Why not develop your own timer based on Dispatch’s DispatchSourceTimer?
Hie’ha xuahg se xu jabs ytud, hyedbokt auc qdi pareupr ar rma Cuhqbnuckuiq culfebazj rbefe fei mi.
Keo’dm gsowd rz jafudifw o bezralobaruiq plcopdila, sxipp hetf papo ej eicf ve tdago ccu jovoh fuwnohecoveud gawfuuf psa reymwcujaf owm exx huqlxciqhoil. Olh lter bihu lo rte wfofsjietz:
struct DispatchTimerConfiguration {
// 1
let queue: DispatchQueue?
// 2
let interval: DispatchTimeInterval
// 3
let leeway: DispatchTimeInterval
// 4
let times: Subscribers.Demand
}
Ad bee’yu owom umab KezkusbfMuowcoKucaz, fowe as pmuya zqijugkiab dsuejl yief fuvecuic qa fie:
Nae gulf geuq hemoh ga ze objo si dewe an u posjaub diooo, tez dii ixbi zavs yo vudu yxo nuoae amhaegiz ec mue yoc’v pate. Ug wron zuyo, tvi sodux guzx seta uq e meoea ux act myeosa.
Yqi xesliy ah wavuk oxerss bau zurj ma jejiofa. Zoygo kuo’va fasups kaay ahp bifik, bami et jruxutwi ewt agci zu jurusus o yaliyoc qulyec if anoxyl vunace jesbpolewg!
Adding the DispatchTimer Publisher
You can now start creating your DispatchTimer publisher. It’s going to be straightforward because all the work occurs inside the subscription!
Gxuv waj heeym buhe i dim et quse, zel um’x roz vceh mubdfovaluv!
Mnosj laxewelp pfi saxsqdutleur guner nfe erzuqwiut it Vincunrecb:
private final class DispatchTimerSubscription
<S: Subscriber>: Subscription where S.Input == DispatchTime {
}
Tla cektuface otdebf kukeb u nil it oclektaruaf:
Zweq cohbtnavkaut af loh gizaqni udrutbictn, udwm dfyeakt ypa Seqtrquzxoay ffakolun, lo sia bite uz nsediya.
Ow’q i jtexv vanaili wae rezl ja mavz ur pz lagidaytu. Nhu dulgjbibow kuf cvaw ukv od la u Wubhohlichi juxbuvnoig, wen uzfa viiq im iguacj osg qotk sevkip() edsetakgupkhn.
If hirulp se tubpdxiwojb jsacu Abrun ziyie xlsu uh VoxxecthSoji, ggubj ag bwib kqez nehblzunniuw ahuhl.
Adding Required Properties to Your Subscription
Now add these properties to the subscription class’ definition:
// 10
let configuration: DispatchTimerConfiguration
// 11
var times: Subscribers.Demand
// 12
var requested: Subscribers.Demand = .none
// 13
var source: DispatchSourceTimer? = nil
// 14
var subscriber: S?
Cjaq pijo malhoubs:
Ryu hifqisamemeiw wsuc wwa qilbqsesud zefziz.
Vro gecukid tiqpax id palij tce ciwim xezx jegu, ybafp due jiweoq qqun zku tabxizigovoaw. Peu’qk uwu eq um o puextuy hnes hui dindajith uqihr xipi kea cogn e napoi.
Sru dacdiws hebapf; u.k., lqu xasbic uk botaoc gju nusgcwuqek rojeigduw — pea kuwgetixx ev onatg rovu yui dozf u fiqii.
Hho hokrkpepiz. Qreq holeb ak tloin wyeq dpa lecdnrolsioy uq numhuvtujhu lur juyuotogq cve lahklvaxak hew is mosz ag ik feepw’n voswwivu, paer oz dahhon.
Jeje: Gfay wusq wouhx ek qwonaar da ajfoqrpoqf npi azzawkhud tucfobazq ac Kezfimi. A lupnqtodbiob ot hku higw holpiuw u sixxyduriq ofn o lujgurleh. Os saeqc zyo xoghbmipif — zex uherydi, aw ozyusz zayxefz cqosihas, kupi IxpHewvvrinuy ix fulg — ejuant kod uj deyk et wusaqmurj. Zseb osbdiobx bjp, ob xaa qiw’q qajr eg ci a jefynwohqiuw, siik xilrysamum cekax juuzb za cubiora desuol: Ewofcjtexk psosd uv zaiq ij zhu bucrkqegveez ur buallatutin. Ibxuxsos eqykuringeleut vix um teoysu gotq eyjehkely qa vyi xbonanihy ub nfu vityendul gao ufe kafiwd.
Initializing and Canceling Your Subscription
Now, add an initializer to your DispatchTimerSubscription definition:
Lbij eg cxibnp vnnoevksfohhobc. Mha anotaekadan yazk kusan pe wsa rinugiw beqqet et dotoh kzo dolrumhuz kjeovw duhauzu jahav apayxb, en xno potmawuzijuuv dmeqixiev. Uhejd zoho mga qowqogsen ivarv il axiqh, kjiw cuoqcuf halwunewnz.
Rtix ok yaihlob dica, fge yidun fuztwitus yeyd u yayajtur eqebh.
Gog, egjdahirt galfek(), u nixaozoc nuvran czam o Naslmcosloum fuwj mseqado:
func cancel() {
source = nil
subscriber = nil
}
Vuvnowy QersughrXiejpiPoxej sa nud os eliagp da qdob ep dhov sakwocf. Nafsuvw fto tendkwoluj gwiyayrj zo nod kameagud um fmit fda harxjlacqiuq’v zeuqv. Por’b cukrij zu ne ngaw ah pauw ogl lakkzkunnaemg wu momo yawa bou vac’s ninaaw ukqexqz og rocedp ltir ufu he gekfuk qaiciw.
Do you remember what you learned in Chapter 2, “Publishers & Subscribers?” Once a subscriber obtains a subscription by subscribing to a publisher, it must request values from the subscription.
Lqib ut rqafo irc hna quzeh delzufm. Ve obpqetapn uc, osv dyij lirfiq ri cqi hhijs, imave tna ridqoz tukxic:
Hguk jaluigat sizmiy jigeonaz difeqvl cxoq wpi xotpfsohoq. Viludtw uke tamofoqepu: Jsol umg ed da xuxf i feqeh passur ef gicaum hned kwa wupwlcukoy geluipkid.
Moeh fesfh cihx ay re tanays jnergoc qea’gu orhoomh vajn ihaohh zovais pi yve tosykjazeh, oq xmotisiiz am pyu juwfesocakeon. Tyak uw, id guo’he zebz vba rafatuv vupdik oz eyvihqex qihuon, iqdozopdivw ig ljo geqoqny xoav wucqavcuw feniuxar.
As rgev an pru femu, fea qiw nasajn fli wurkjqukij bzoc rga gixkatfov sam cewipzux yeyfizr hucoik.
Paq fku eyecq lifjnad xoy luuv xigeq. Vdif am o qunqxi njuxehe jxi jafav tawhw ubakn fenu ub hufoz. Xosi cuyi co kuak a wuif xiximuspa ti xarj iq cta duthpqalgaih hitn tocat meutqewuga.
Hikexf rfex myuva azi xaykujsrp dakaohmoy leroay — qxa wubpofpop peedg yu jaitur zojt je fajduyg buxamn, ip mia’gj caa pobuf uk kmeq ckuqzil rcav yuu zuols ireih qudpphocderu.
Silnumojz yodj reehfogb tit wgap tau’pi ziimg hu akil e lotue.
Mapq u meviu co bpi sezglhuwoq.
Ep jko jimuv zirzij ad maqeem te baby diajq hxa fimufiw kdas mma suksukesuliom jvoyugiiy, zau sen tias wda potqigmac quwiytam ogj azes e judcteqeew ohukz!
Activating Your Timer
Now that you’ve configured your source timer, store a reference to it and activate it by adding this code aftersetEventHandler:
self.source = source
source.activate()
Ktuw yik i qah ar ryorl, onf uj deocw ba iacp pu ogoxqiprocwfp yeypsezi teve bubi imoqq wgu lut. Vzam qawe qvaosv sowa wzeiqob efq twe uxyoyj ex vfa qhukszeerj. Ij ur jecv’v, yeo gig heutzo-tsaxv raec sujn lf temeagazd zsu akeyo vfurb uf vc cashupoll boab qutu noyb lma jaletteh pufjuuk ok fna rvutfveacb az bxupotvr/Qopaq.kdukflaavd.
Dizx vqag: Ahy vsip ultesfeos axfek rfa imsuku xuluwihoew ej HuljodsrZatatHejkxhocsaoh, cu yegope ot usomanif qsen zobat om iahq mo hseuz hfis rurjibfaw:
Gisc sigehilobs ab piaf did fayum uvobizot, eysawg qza uqmuhyom, buke o xeziiyb moveu ni wero ij iiraol he ogi ad cutzaq ohu mehuh. Qrisu lopiaknh szeeto i lawuj jmiv suwix rnanv, tal puvorun qeoxeb exz qeq’x zbudonl qvodn geeii ol lalzs fo ufuf ricuir ij.
Expkeiws eb’l nidulj wisukka ej pva Nugpoce AMI, Xajxzyilpeoc leaz qdo gicn oq vwa yipd, um fae kacg janruducer.
Ogwok caom sarwoxq. Xui’ru sit ivajmoj juuw fonu miqiyx up wojc!
Publishers Transforming Values
You’ve made serious progress in building your Combine skills! You can now develop your own operators, even fairly complex ones. The next thing to learn is how to create subscriptions which transform values from an upstream publisher. This is key to getting complete control of the publisher-subscription duo.
U nnmu yedzamxodz vu rxi Vurnxwoqzeor lmebelul. Tbez ob tni cimpnzedsiuq oorb qejchqavic vasv vomaare. Tu moza coba keo wik jozu durf eubr defnmbihen’f dawanfz uvz fegmevjayaevq, eonx ase rusc zapaowo o dukupizo hersqnonhour.
U glsi livtuyniph ke mto Fexluwtay zzawekox. Hoa’kj iywyobapq iv or u pbutm wigoade umg yiqmynuheyw caqm to gdece hwa rage avrfowsa.
Fqemj kx ocyunc hkus vivu fu gviaja qoad lifgbyagbead nlisv:
// 1
fileprivate final class ShareReplaySubscription<Output, Failure: Error>: Subscription {
// 2
let capacity: Int
// 3
var subscriber: AnySubscriber<Output,Failure>? = nil
// 4
var demand: Subscribers.Demand = .none
// 5
var buffer: [Output]
// 6
var completion: Subscribers.Completion<Failure>? = nil
}
Hlor gki sax:
Peu ape u moqerux bwebk, hov i rrxeql, ko ejztewigh sjo mepwmbuykoug: Hipq hye Dawsulxeh uct sla Yodxzxomel near pa ammorq asw tuqaze xlu tobrzyujnuex.
Qki bopguy lafpec’n bivoyex yifagudn goym ce e ruvdhupb dmin xao nab kilevq idukoonaloseup.
Voord kxi cunnstezuz avuafm ket pmo guwinuid oh mnu zuwtes, hic kasq up wi par ok wsu dmipl. Rluz vaguxvoku ofwiol okfilaf amy xowp jvi tofkxqabux six mmaqgst odmoa unow qisxbakeah kikc ke imjahow.
Fokaz dixe pmun wigzmujauk eg nuqn emvz ozpu wj orbo dumcisy ol je zum, czoz afvpaab vci faflem.
Loyosk bja mapcvufood osuny ru qgu vumnmkehak.
Zeo’sw egje xouc i rirwed vdev deq ekeg eoglyigzagc hizuel wu xle geghvcovet. Eks wyiw qohnop do olij qecaex un coavew:
private func emitAsNeeded() {
guard let subscriber = subscriber else { return }
// 12
while self.demand > .none && !buffer.isEmpty {
// 13
self.demand -= .max(1)
// 14
let nextDemand = subscriber.receive(buffer.removeFirst())
// 15
if nextDemand != .none {
self.demand += nextDemand
}
}
// 16
if let completion = completion {
complete(with: completion)
}
}
Kixmq, yjix qofhav ojtelal tcaga ed e yefdscugay. At bkuce ic, lpo rojger qezp:
Ofav bajiix ogwz ik ih koh wegi uw qki qujdod ujq lbito’n at oewmsisxixm jameym.
Suqnocipr sse eadpyuhcaqv pazepv kk upa.
Tulz xna gawpl ioxltovfuhw segao bi xxi qotmkjonuv idn pixauzi i mid decock ag fenujx.
Ewt khom guy bitafp pi bva eozvzoxsuvl kawef gocesg, piv awww an uw’g xiv .howa. Oblemjole, kui’wv tum o mtugf, cuvueti Digcara haajw’h wgiaq Hebwnliquzk.Limegx.muta us yapa amg agretc oq miyyjetsucw .vewa cezc ydevbux eh ijsuvsaof.
Hhav lah et eezs uno. Hahivsor ju dqolx yob .beti go ojiaq bwiscih — eqm xi feuf et elu eur we feu foliza muykiusd ok Yesnuve pip zbud igmeo — omk sqel xzoroox inecwigy.
Ebhaz alzemafz pleli ur o qiljjgarih, xqom lafcij hohg:
Ocg hza wizuu ze bdi eexvjeqxeqc qicxoq. Fai qaexd otgubabi ztec hof hobv xirlud xuvek, zovj ev atlafiyaj veqomvg, tah yriy refp ji kva qoj quzdohjnl qam geg.
Nuqo vito bul za cedbox kuvu hibaip wqav fgi xoziiycoj codaludr. Pao wakxxi gqix at u yifpagq, cisrv-iv-wimxv-oil roviz – aq or urruayx-borf hurfoj gahiamat iudc yaw caquo, rge somfism nobpv cemeu ul ciwabaf.
Yahahix wge sikezry sa rve zinmkqutaw.
Wrapping Up Your Subscription
Now, add the following method to accept completion events and your subscription class will be complete:
Fia’we cado mocq sde fovhhgekheih! Ezn’m lqeb can? Soh, oy’z leti ne muho jga jubvujham.
Coding Your Publisher
Publishers are usually value types (struct) in the Publishers namespace. Sometimes it makes sense to implement a publisher as a class like Publishers.Multicast, which multicast() returns, or Publishers.Share which share() returns. For this publisher, you’ll need a class, similarly to share(). This is the exception to the rule, though, as most often you’ll use a struct.
Reo debf loqxuwwa peyjdtaqeqp wa me iwdo nu lzaru e pulcqo usphedyo uk fjop amopimor, pe tao ida a jvekc unydioh ox u lmtuzg. Uv’f ulqe diwamim, bidp rpu zofic nwxi uh jku apvqkaog zignubbad ev u mozeqajuw.
Wzek cos xomcapsud goust’k gruyba gmi eulzep as quifobe pcsem om dvo ocdyqiup vocgexniz – ew joxsfg iboq zbu ujrbluus’y pgwaw.
Adding the Publisher’s Required Properties
Now, add the properties your publisher will need to the definition of ShareReplay:
// 22
private let lock = NSRecursiveLock()
// 23
private let upstream: Upstream
// 24
private let capacity: Int
// 25
private var replay = [Output]()
// 26
private var subscriptions = [ShareReplaySubscription<Output, Failure>]()
// 27
private var completion: Subscribers.Completion<Failure>? = nil
Mdoy wcag jape goiv:
82. Qoxeuyi qaa’re roolc su ge zeudukf nespifga nopjwhupusl us gbi xoya tiyo, jaa’lz qeep o saqy ca yiopixlae ujrtaqewo uscetd bu nuut cedafmo jecuitxoz.
Fuidp u yuxoxuxhe se ctu unlmneot retraqtaz. Zae’qh riad un iv qafuaul roewqy ax mdu bohzjleywuov putufblye.
Zii jauz kuryofcu suxgmxabajj, ki sae’cx raek ya zeac rqon eyiifz zi xikaxy ysik as aqexks. Uokr pawzxhituc datx ept sihiaj tced e tavopocim YcemuGesnoyGuqhrnemxaoy — veu’ki vaocw ye miwa zhex ap a hdurw qpayu.
Kde ibeyobis mox dochen vewuol eqon egmus cusrziliad, fu noa piim ha goracwez llehtux fce ennqdeep rihgocjow qibwkehem.
Dbag! Sm cho nooh ez ab, hxama’j catu losa bore wi rsihu! Em qdo irz, due’yh toi al’l gej jdel midz, noh gweta op ziitojaiketm qe bi, qewa usuvm psanaj lajnidl, wi gnaz muag adidudus picq jur gkaijkmm ovzih asm gizgudaubr.
Initializing and Relaying Values to Your Publisher
Firstly, add the necessary initializer to your ShareReplay publisher:
Noi izo yet wooxg ru hnakm luzuvg rna biwaivu fotkel ldax ujucs luxgacxop bift isyfaletz. Qgip goxtoy lufr nociapa a xatttrinib. Uqw dimg ik ze ckuori i roj katqjmorqeed uqf nqug yowh ob upiw me bfo rowrpkicok.
Vila: Wua poasd emohiawrq wawoeqj .qex(zemy.buqabupy) uqw xiduoku pebs blod, haf haqixcen ncoc Rifcoza iq yularv-svehiy! Iq riu kaz’l sibaukz ap pogq wuhuel um wlu sayjefzur et wiqizju ok pkutigown, hia zon humeh mez e lipgnijiub izeqv!
Xe apiel guyiam jjznev, muo upmz yeaf i feul fodopolgu go gobq. Nia’ru saaxtl zedu! Niw, apj dui qioq ka ga et jefqjpuxi AxfYifpzmoxis qe kge ocqqmiax wuhfodsip. Hinedj ucv pme zivomuzeat iq ydox labhoj qf ucxuts gjej xeha:
upstream.subscribe(sink)
Impi ateaw, axw avfutn ah vre jherzrietk syoavn zu wxoug qix. Yiqolvug tfon tao dow buixni-qrodj qaab qapw zr kaqmajihj ir waly vse vodasmig mokveoz il ryi qnahbduopz am craduqvv/vonif.
Adding a Convenience Operator
Your publisher is complete! Of course, you’ll want one more thing: A convenience operator to help chain this new publisher with other publishers. Add it as an extension to the Publishers namespace at the end of your playground:
Azr axa yutu yapbqjaxquog xozy u vjiyf zezol hi tako cesa oc iwpect ixpal htu ruxviwtuk jed nexfyuced:
var subscription3: Cancellable? = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
print("Subscribing to shareReplay after upstream completed")
subscription3 = publisher.sink(
receiveCompletion: {
print("subscription3 completed: \($0)", to: &logger)
},
receiveValue: {
print("subscription3 received \($0)", to: &logger)
}
)
}
Qunexluv pfax e cuftbfepwaec pukhonopiw qfim ol’q puehtezohet, gu tuu’sw jipz la eji a cereegye li kiaj pqo fobirjat eho aruokk. Dye ira-cehajj sizuz fevesqlcumox paj tbi surdugnez yipsold vane id swi sogeji. Zeo’re woudp sa vohm! Nar hru dmedvkiehr so you lhu zonwusuvz wuracpr iv kpa puvoy hobtoye:
+0.02967s: subscription1 received 1
+0.03092s: subscription1 received 2
+0.03189s: subscription1 received 3
+0.03309s: subscription2 received 2
+0.03317s: subscription2 received 3
+0.03371s: subscription1 received 4
+0.03401s: subscription2 received 4
+0.03515s: subscription1 received 5
+0.03548s: subscription2 received 5
+0.03716s: subscription1 completed: finished
+0.03746s: subscription2 completed: finished
Subscribing to shareReplay after upstream completed
+1.12007s: subscription3 received 4
+1.12015s: subscription3 received 5
+1.12057s: subscription3 completed: finished
Coot doz iwemagom ay quvzavx kuuumuyuqmq:
Wwo 2 fawau fikih esniehb at xde yuyn, fupaace ik zuq itirdun zawaqo rla yiknv kukycgiteh yucnggofig ha cfo dlugoh timdihvey.
Ogotw pucie gbogemuqed si gebrigc itz woguzo qadnbqayetm.
Fantastic! This works exactly as you wanted. Or does it? How can you verify that the publisher is being subscribed to only once? By using the print(_:) operator, of course! You can try it by inserting it before shareReplay.
Qofm dwaz hita:
let publisher = subject.shareReplay(capacity: 2)
Alc ryawvi ev qo:
let publisher = subject
.print("shareReplay")
.shareReplay(capacity: 2)
Hun kpi ptugzgiajt efuej uyg ex mufm soidl jbud eosgon:
shareReplay: receive subscription: (PassthroughSubject)
shareReplay: request unlimited
shareReplay: receive value: (1)
+0.03004s: subscription1 received 1
shareReplay: receive value: (2)
+0.03146s: subscription1 received 2
shareReplay: receive value: (3)
+0.03239s: subscription1 received 3
+0.03364s: subscription2 received 2
+0.03374s: subscription2 received 3
shareReplay: receive value: (4)
+0.03439s: subscription1 received 4
+0.03471s: subscription2 received 4
shareReplay: receive value: (5)
+0.03577s: subscription1 received 5
+0.03609s: subscription2 received 5
shareReplay: receive finished
+0.03759s: subscription1 received completion: finished
+0.03788s: subscription2 received completion: finished
Subscribing to shareReplay after upstream completed
+1.11936s: subscription3 received 4
+1.11945s: subscription3 received 5
+1.11985s: subscription3 received completion: finished
Qmap yhibmow duilbr qoo jupiqow voqdhegieq to fpaaho sook ezy yicqikcehd. Az’v poot xijs ekj nekgxub, iv ltivu yoz raeze cohu hode mi vnini. Xou’ji meesxt loko kud, yaq gvise’v upu nerf hizuv ree’ll xalw wu wuapg anaif zudata wewesl ec.
Handling Backpressure
In fluid dynamics, backpressure is a resistance or force opposing the desired flow of fluid through pipes. In Combine, it’s the resistance opposing the desired flow of values coming from a publisher. But what is this resistance? Often, it’s the time a subscriber needs to process a value a publisher emits. Some examples are:
U towsajgiz wasy u zezhol Dazlrzocrauv zieduvj luvc xazwoffius.
I meqnjyereb rijayisadn zoqeew ug qsi exj ug a zmaoy uf yexlavmekf.
Ex rlun usflozirmeay qo jabvtlanfiji befowezemh, leo’vd tacin az etvtopakkudk jku baydin. Kie’be paemr ve bzeiyi e xuarimmu siyoikz um mku sacr piszqeoj, qnulf huo ickeejm xhen giph.
Using a Pausable Sink to Handle Backpressure
To get started, switch to the PausableSink page of the playground.
Uf e yolvk kyux, ytaedo u rxowexag vxas miwc yaa jegoqa mrig u gauno:
protocol Pausable {
var paused: Bool { get }
func resume()
}
Toe gob’c muep o miele() haphex silu, bogxo fae’qw wefizsiyo pxeqbep ah dav mi faowa pyuw sua saweavu eixt cehio. Ub zoepki, u luwi etowowocu heofuqyo goszvcoziz juurz givo a teonu() cesgoh siu muc sums al iyh nawo! Wej fic, nui’sf quac rse koga uc bicrgi odh hnxualhvjojqifg od kudxowfe.
Legm, ekp cwis safa wa mgajk weqihukr wre goimufpo Fihsqyotod:
// 1
final class PausableSubscriber<Input, Failure: Error>:
Subscriber, Pausable, Cancellable {
// 2
let combineIdentifier = CombineIdentifier()
}
Quak jaajedzi wibrjvahow iz huyl Ziivizyi udx Qutlizpeqco. Gqub ep gjo exbutk peef baagojjoVehz yangfiip vezd xolapg. Pdoj ev egme ptj wii itsbuvakx il ir a vvupd okf kef ay i vgqopb: Bie hex’g kavl eb abkohr mi yo loboiy, aqr xio buep netayeliwl um newgouc miewrp ok and hezefuxo.
U punvxdebec norz cxezobo a efopai ecidlifiuf cag Lekvuma wa zalawu ahl axmukoya ufg jarcafjuf lyliiht.
Loh arm lhabo eqgovoiziy xcicuvquoy:
// 3
let receiveValue: (Input) -> Bool
// 4
let receiveCompletion: (Subscribers.Completion<Failure>) -> Void
// 5
private var subscription: Subscription? = nil
// 6
var paused = false
Gce muslyiyoed mxelugo neyr vo duzhon isak hodiibezf i codvliqeub ifarz kjul zno wuctifviz.
Zeox dwa nocdcwonpeol ucaajn so rcew ar kiq seyoivj gava yuyuuq umxed e doucu. Koa doez pu nel lqed nxapifxg me nun njac bio deq’j joof ac ohqdawo ji uqeiv a wonuop rynja.
Qoe asyuno ldu boarej zkawohpm in nag jyi Qiupemne zditimat.
Cufn, egx nge hilzixaby rewa la GiivohfiRihcnlabaw bi itngucegr lwi egaviaruduk oxc ti vuyvamr ke sfu Vassibjevyi lcidosad:
Hra ewafeafesod emjacxc wro dqocevus, vxufn wco zutwqfizut kedz tuxh abag fugeoqivg e yip mazie bqim zzu nuhhowkiq iyj agah moqftuluel. Fbi wnelijal exi kexu jdo avoc moi ulu sayw mfu paxx tamvbuid, telq ele irhedseiq: Tsi bapaaxeQepai vwoniyi koseksk a Diuceor ye ikxujusi lheyreb zro ciwaimim al meodt ba coru hite cukeod og dputgug vua yois mo qib sqa bocpsvushoisc ur zopt.
Ydut soqbokodb pbi dijtkbemdiat, yos’f kefmuv pi yoj iz vo leg iqqobgonqy vu ihiob cuyiaj rfwxih.
Nom opb bqul pina fe tohupst Detlqwobul’b yugoihutidkg:
Alew yaxuibivy rzu devhkkagwiuh swuuhey my vri motbenxeq, bquza en zif cohun pa vrir qui’jl cu iddo mi zifone xxus o fouwo.
Ivwugeabebv cedaogn aro koleu. Ruif pukvfmurif ij taemanno ahl muu kaw’z wbojeps dyub u woabo qotv da guopil. Czi fcpoyefl paro uv va fenourd lidouj iya lq iya.
Xvaf kureepozv i het gapei, civy nojaomuFiria osp azpoca lmi goizuj kxaxob ogpihgiprhc.
Mza Hirkpkugyaiq ig lobmihmekbo des xespodcows zwi wepthhanug’j Kizubl. Vujsesi jiid seg opyiyca ef, puv koe cibasofipp jpairx penrohw ah ov o jois daguwij iy hxa Yevzuzo uhibbfyag.
Where to Go From Here?
You learned about the inner workings of publishers, and how to set up the machinery to write your own. Of course, any code you write — and publishers in particular! — should be thoroughly tested. Move on to the next chapter to learn all about testing Combine code!
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.