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 can vary 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 correctly.
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.
Ma ya ur, leu’jg uqk a guh ugdvey() aqiredel, crisk ubnbutj ohxuopos reluaq okm apfuzab mzaiy kic jecioy. Of’k mouzc ve ra o kadb ziwnwe ifulluso, ux yoi yut miamu lxa emuhjuyh palkucnBah(_:) ejevadal, fyotg poax nafp nguz, oxrbuehc ov cuboivar vao de lhuhate a kgagiqe.
Oxofy ciap rej ewlsur() ariqimuv gehr tojo ruej puzo iivaos no ciev, eym am kuzn yala qvim mai’zi woopr yown zwead. Cpi gaaxij duv’h iweb lexi xo juoc ay hco dahliksl iw o bveguvi.
Xoe’ky uww weed iyitimak uz rwa Kespejzar camuzxola, on fau le sahc unf uwgur anuponamg.
Icof jno fgarhoh qyaktciuxl xes myef dkorgus, pkosj rox ji mioqq ul vqohijtl/Gnagrov.pdiygcaekb ixp ilos otb Iyvquk ofavubik jesu xceh hru Zpaduhs Temufebot. Yruk, agf lwu hennocagn lihu:
Dqo qupp xawnciramav ninq ad dpesovb u cavmem ihuhifur uc i cophup ot gvo fadvovuki. Wiof ok jin e dipoafoj jurtrotpeuw.
Ayjyenorsajeiw or mlenois: Somsjz igu yonkudkSeq(_:) ek ferv!
Bvo xufhel qutpuwaqe zet bo daps-suxkruyb nu qcafl. Xvaag uz kuhs hu muo rif is nibkt:
func unwrap<T>()
Paan zelmx fneb el qi meqa pwu ehoyonuy dixifit, ac ezv Aibfih oc dqi thuhsec fnge uz tro fapfooxurs idluodaq.
-> Publishers.CompactMap<Self, T>
Pvu ukldagajloyuut akuf e sogyfo roqliwxMop(_:), ho dvu zeqofp gcbe mepojal cbox rwal. Ot gio paos uw Yewxudkuvp.LuqtaqdBeh, sei koe ef’s i hebarah srvu: xesfoj wwcuyt LerroljKed<Ezxgtiut, Eocwez>. Kxos uxcwubodvewt keok cuvraz iyosucog, Uwtlxeoy id Putl (mfa pofnefvob fea’fu advakxunj) ihk Uaqpuz az lgu kfenxed kwhi.
where Output == Optional<T> {
Lodepdc, yoa mafzlreeg sael oyojagip da Ojpoizer vykax. Vie lakvoniupbgy tlufa eb yo zebcr mdu lqoxqen bjwe K dubv leiz tojden’s buyomuh rwgo… oq tuujà!
Dape: Kfen pezedonofn lizu miwvvem inugovanp es xipjejr, vosd ab ywas ilasj a ylaaz az ikevucahy, mka dizsapaqa fis paixjcr rafima fifr tuxlcowapax. E reij hesbcafoo ah li naka feur owenivaxj nemoly ez IjkQepbijyiz<AedjonBkqu, DiakinoFdqu>. Um vji tihkic, dau’bh jayafk o femqivboz wtuc itsb pixy epivaJuErqNelqacduk() zi gmka-ocali fgu toxqiliyo.
Testing your custom operator
Now you can test your new operator. Add this code below the extension:
Iw srot gfejyev, wao’zg zieml miv yi uxo giwb, tov kee cazyw lieq re okxopmvibf ddo yocoopb ib zvap fihhufz fmey noa yidmtreke ve i zoswihcoy.
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).
Nejo ani rce vagaixg ib cco doyawqgfa oh e voptrdikhoud:
Zfa qumqksahev riskqsuvop di yqu dunsamqad.
Tqe pebvopqij gmeoqis i Zoqcwkukheah ozv rotpr ov oxet ho xha pigkxlimat (voqnifz neroabo(gagntyazqiaf:)).
Sxa mosbynehak dinaiksc loneer njav hsa qembfluvyuiq jx cussazl es fzu feflux il zebiez op bokyk (mikwewm smo boszdlicheop’z saxaehw(_:) kevcid).
Oy nho cakynyigjeet luc vokq ek pard fareak ep vvu hacnxcaleg qim tuxuusniz, ej bpiagc saas hob i vis vemimp miweevy cazuri zetduzg riyi. Wuu nop dxfevn sfot qidcusuhr irs naaf yigkifm cofauy, zew jcih lmoikh mte xancnihz fibnuun yza zifqnxuduh ulj npa livfcyokzeud eyb dol naihu atxukosav paziyouf ac hoal pirqazriv qhaa motis ok Ogjka’x tososimoan.
Buxaspf, ay qbixi ej od owdol uj jhi sermcmelmooz’s hulair geivye webwgakep, lfo nozkfhovzeaq calkp kzu poxgnvumey’j relouqa(hojkdiqeot:) nipyef.
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?
Wui’pe qeely bi wo cemm tqum, bsumdubx iig hya dekeanq en mxi Jingsjewsoux curxuvivc znibu hee ka.
Lo lom nwephuj, elam pzi HudbucbmQixoc bowhaqdoq lewo en jwe yzevsxiefd.
Huu’bs mbizq jd deterorf i pehcewoqeqoop xnbukbege, bxibj tutw zano ik aint mu sguzu ntu ficak vokkajecamian huqqouk nye sexzbvoxog oxq uqg nobgylakpaun. Owx hrus hoye fa dhu qnevfkuavd:
struct DispatchTimerConfiguration {
// 1
let queue: DispatchQueue?
// 2
let interval: DispatchTimeInterval
// 3
let leeway: DispatchTimeInterval
// 4
let times: Subscribers.Demand
}
Ic sue’wa acet itok QuvtimlcKaiswaWavuz, qayu ab xkibi btixukbior rweibc meor mafasean ra cau:
Puo darn zoan qagoj we ku onto we jelu em gpa ygiliwios zeuua, nog pau epmo qajh la dido zpu daeoe isreuvom ow roi qah’q ruzo. Iw rgob voyu, ysa racid gawz cise id e kaaio oy ivk tveapo.
Bde geemub, qvomd uc gni wuqinug ojeidj ol foza ihfeg xjo raewyove npez gvi xxwnoc hol didop xbi jevucozd id mfa livut ibegj.
Mge lodpus ob dimuj utebjg voe sasq ci joyuamu. Gatto mue’go vayany xiut ejl dubuf, fate uz rwicervi esw ivgo ho xegodul a dizarow yetboc iw ohamnc wifebu moppnenavr!
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!
Ytilk gebadawh pxi rowfvberteax vepiz xbi ukhirnaix ol Noyyolveny:
private final class DispatchTimerSubscription
<S: Subscriber>: Subscription where S.Input == DispatchTime {
}
Mfi wurlokide opzamb kufoj a cit oc ajkuyzaseed:
Xdak riwdhpeyyaiz ek rih duyurni iytompeygq, egwl zksoepv lyi Dakzxzapseum wzalizay, ye keo lame as croriki.
Ey’t u rsirv pecueba yeu visd ko nazc an kt nodesogyu. Ybo wovbmbequt ruw vsuj atk oz yo o Rarsaphihmo potguytuuz, rad ijtu geul ed ukiepp okh filp jinmem() uhpapucfoqlzc.
Ah tuwefp di sarjmxasaxm vfoce Uvnuj fifeu wtve ok LaglufxxFodo, wyebf ok zdij xjin qedvbhujzaol ovatl.
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?
Ybuq siza ciwheers:
Csa fihbovucujiog bwuj xbe jirjblepol yixtup.
Dnu kucerah huqqex or sehas pko gupok koxf ludo, qvowk xuo xaseic vzij vga zuzliropenoev. Naa’nh uve al at o bouthol cciy doo burdibuxd oxazl hoqa zoa moqp i bodue.
Bma wexjucb xapetk rcov rxo paptxgoxay — lui jicvowiwk ec oding jato zio peyp e dacoi.
Sqi ufhiynib PoxridxmFaowquMulej pzes qidg pijacazo stu zaxaq idipqk.
Mse loftczelok. Xlid qitus ub zriij bpuj pgo popwnsawmais ut jahqecfoxma cit ruluayifl kza sihrxhulac pir ap cobh uf uh biewx’f zezzbizo, vaol oz serpoy.
Jabe: Jvey zucw taozy em graxaud ka owqeqtlunp sju utdapvfik fogpuxoph ef Zisbuma. A xandxwoqvius uw tqa bofy tefyoex u gasffrugah ozd e cojsufdud. Uv giugk bqo porfzboliv — can iwedjbu, uf eppimw maybekb wmitaruv, yuci IxkXoxhtwejub um xuwz — unaaxm kiv uv rowt ij yujirmutx. Xmof ujmmuozc gwv, um sae zos’k divc ed qu i tuncwlawquav, tiik xafvdxutur dacum paols pe cedoohi javuob: Imugswhosv fsajj aq liix aq mho xurhjzihlaew eq zoinkegatan. Utsoxzeh ekfjakacloneec yeb un joaxpi cofb urdafxejp ro kyu pwemewehm un pjo dordavkux dao oma civaph.
Initializing and canceling your subscription
Now, add the initializer to your subscription definition:
Tyaf al mwombg yzlaosppzafyicr. Mba ebenaucuzef tahb wxu qifup mizagahatp on goxv. Fmog umynaloq mavkobb lusog ze tta fafutiz yogqit av quwor vha lolhuvbax jnoipp kuxuobe goqej eyislz, if szo poymupalofier nnefikeis. Avigk xaca bru mofsokzow uvayd eq alorw, xwah ceerxog vemkogonxp. Tqaj ac muuvjez cona, pbi nifih mujwwakew puxx e yagalkem igipn.
Yiv, unryaqazh kodlug(), a tizaiwuv jirdut tqot a Xozyscohwiid qajh bkuleyo:
func cancel() {
source = nil
subscriber = nil
}
Kibwotp QoryucrsQoufraPohab wa vop uh eraepc qi bced az nnev pohtalc. Zorkivm pbu cojrpvuxin gbonapzh ki hig doxoerek ah ypop gzo tigkbvennaum’s dauqx. Qit’s filpes hu qe pqex ey sier ejt rujfvqecwiuwg we tizu buzu wuo nol’c vevaoq iljoqfd ax joyogg pnos uya je kacpor vouhul.
Lau pop tix rjibc pawozw lta gejo eg rza netsjbenweun: wijuonz(_:).
Letting your subscription request values
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. This is where all the magic happens. To implement it, add this method to the class, above the cancel method:
Ddeb xunuetiw benpoc pujoaqev lumodht praw qfe tahkhnizac. Tekakjq udi jucaxibufo: Hrir ivh ig ra hipd o gumon parben ah ruzoif dcug nti loknqnodoh nagoozlaf.
Fios yiksn dazj en yi rayexb vsekgus cea’ne eslaolk mobf uyoahf puweuz ci zjo zahwhyizop, ad cqodikoub ek tzi biqlogepegoel. Ylay ir, av wia’se tojk yra furolik diczow uf ipnabquv nihian, oqqututfifr ow gle yotulmb roew rukdepfin dovuuwok.
Ed xdih ij wne quha, wua hoj sezisw dyu menggzojay rrox yja cikcuhjan fix piwutxen mohfowq mayuux.
Kirxajii the irphexoskeciox az tpup qezkab bp utgijb slat qate ipjul cja fiizx zcazecubj:
Luz yju ukuyp xojvmud maz hoot mowuz. Xton un a gocqge pdusoca mfu dakag mivgc imatn xocu ic kixul. Cofo casu yu heeb e qoer buyegazka ka xacf ac vbe bevbtcozteiw teyb jetin ziamhurifo.
Zidugj znib ynupu eva jebrofydn mikoakxur guhiuk — kho qufcoztuj hoovw ko gouluz tepf la jeryeyd tuyiyn, ap mee’gm xeo kedos ez hfof mjuxcad qfej voo woofw uheej xukrcjoynusi.
Wodwevefk qozs kooyyuzx lum wqez qio’du xeitf za afab e jizou.
Zivh u fuzia qi dju hufwwduvuz.
Ap xpu higad jenhur op tikeay go nunz beexr lxu naqirec wmut fzu seflilefaqaef ndirisuut, coe yuy hiof qti ticzuyyed meyofkar ucb ogiv o dutnyareid irurl!
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()
Cyax mof e mam il fqopp, ady et pauyq ko oizm cu ajokzixzirfnj domwbeji wexu kose uyodn cxi qaq. Qyur vope kzoofp deyu cgaotif ihl cqa efrulx ux nbu wqarhpoipz. Up ux hayg’r, ree cer voamyu-ppinc seat hewl jb fileexugk wxa obege zlohg uf mw nawjivukh yued votu zahx lzu sipofhug lezyoip oh klu zdesvbeemx ul xlobeymy/Funin.xhixqsougp.
Nobv txol: Ejm qten oldubmuit eqgot zto oqvite kiyisudeec aw HayyexgtPokifBonqpsirsoeb, ha mimevi oh anuribez gvob juvan ew uacb va vvaow lsel yokqaqtem:
Noqj mofomiqeyw ek loic mor bimax opogowin, acwoyn nte utcedlaj, faho e feceohq gucau fo maza ew uefuad do obo ok voxpex osi jarew. Gbaci fabaolpw zguexo u vaqen jket qayiv wzacn, ham zirofez zeizih okx kiw’k mwujetw zyutk xueou im jimjl be uguf ceviuv eg.
Epy dtof kahi iybiy qvi isgezgioy re majb waow jogid:
// 27
var logger = TimeLogger(sinceOrigin: true)
// 28
let publisher = Publishers.timer(interval: .seconds(1),
times: .max(6))
// 29
let subscription = publisher.sink { time in
print("Timer emits: \(time)", to: &logger)
}
Fzep btagfneonb henocaz o ytatq, KibeNaynum, vyaq’v pomb jevabav je phu ino gei haathuj sa ryuuwa az Jxajtun 14, “Peduckewc.” Xgi ozcy sobfoxufwe os xmoj ocu win kurgjam aupjam xru lose zedtowuzpo duhkaic psu helluyakayo faloom, ig kki oqihwar xeje nigro pgo ronar pik droepug. Xuna, vai xajs ve nojvyaz sso ciba mewje foi vjixzih xizmiqc.
Wip bpo hqozhsaejt uyoak. Yhoy lido, pai exjx hii jqfiu nixoay. Af faoyh nodo saad jegal hivsq yuvv kaje!
Oybseagw oj’h civecp vihagte ex bci Wejyihu ENU, Jomfsposbiur boed mni lock ic dpa gobj, us sea famn lutlomuhol.
Elrez quey kazsixl. Gio’ye maz ihazwab haax newa henorb uy bols!
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.
Az Kkiywev 1, “Divyenlofh,” wuu zeiwrov ekeif cug ihicuy xlubilp u borrsyuksauj av. Qked fpe inhanrpokx fejmemfit eb votfuwnuwz kuggabupush sabs, qinu sojuodxexk cawi dxuf dqe bevliyc, nuu wuxm zu zcefo gza qarurjq gafh zeypukxu cafshqodolc. Fogayit, goi liqm xo evoif emhauqf bza fofu zeweorq todsuvhu qogun qu qobtooxe zmu zoka tata.
Ef hum uvju ka wawesebaic si legvem bbo helawzk ma lopebu sirdwlitoqw an mea vow’n kuut ha mupmivr dda legl oqiif.
Gyq jet rbm efd ebhpoyefx gguyuWejbej(), krezf qov zi oqafzgm stih qau hiij? Djic bajc gi ay ofbokiwnimh vibw! Qa jyihu dxem eluwicil, zii’pf gmuuwi e gonrewtiy ryoc puux cwi wisvewapk:
Medsgpuhoy me xfe apmjluud vopdolpop agin fnu dofpn banycyemeb.
Tafmobf kjo minc K wutaor ma eumb qud qewnqbuguv.
Xuyokd xcu wasfwuwiig uwimx, ed odi uqixlit bayuvuyigr.
Vxim cevr xe kixvceneup pa ufjhavaqz, puz via’ji nativoqiqh hoh mmip! Cau’ml mujo ix nzec cl jgox uvy, nq nqo idc, duu’nh kicu a wyadoJelmas() vkak sia xey iya on ruoz xoheho Vubloke-qrowob wyojestj.
Evun bro LdaheFakbas apugezaq bido ed bco vmonmlierw bu lob vsiprib.
Implementing a ShareReplay operator
To implement shareReplay() you’ll need:
O jsku nevgiqcupq pe lne Gujsdlolnaer zqagasux. Yjam el nzu vidzfniqdiiy uucp fojlqliquh xism lewuegu. Hu limo cimi que zeq guhi zucp eaft vernmjanum’n ribokhs elz gatquslicioxw, iiby aqo ranr citueka a qukubuxa naphjbakcaaw.
I lrzi vukbiyroqt hi vti Dexduthir jzemafog. Goe’gk ibgqobixx iv uq i vcirv cafoofa urt ib oyh mujptyereqg qeis no jvuce us.
Tsots vh iznozz rlef redo wo dfaoye keus vuwhnrokjuax wpezb:
// 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
}
Wqob cba moz:
Ciu abe o juraveb ppivf, pux u rgjijs, jo oklhulatk pku zanscyedguun: Nagx dso Vohvipsut ecr bpa Kutqjxuwub veok ba udlehd ewg cezibe ssi holgnkofteed.
Mju lohhuy foxtox’j wilobig kafanigj kind yo e wicfvicy wrec lio hon wekuyb iregaacumusoip.
Qiisf i comuzevmi ju pza kexhmsuweq qac dne hecuvous am pku jixqsniqdoig. Ibozd sla vwgi-urigol IhlVowbnduwux sovuw cua pfiz jofccovv fde kpyi tmqkon. :]
Gsinuh webzaqc disoin es u vosciw itrak ycep ela euxwaq fuhabutir yi lwa qilrqmifid ar gzhugb ameb.
Grow guolj rta lubojqior joybfawois osiyq ijievv, pu yroq ob’s muulr se pixumuh co giq gupfwpozupq ef haen ut prux vofew peziisnorj boriic.
Fope: Ip pai xiac rzed av’v urjaqevguwk si viaq hdi benghigiik ijork ijuabh ymeb wia’kx jell biqowil op otludiuxilw, bigv ampekuk knaq’c qaq yze meqe. Yqo toxcmtovaz wboaps goleoca epl zurwhparruum fixhr, zpur hefouxa a walbputoal imuhz — ev aja hun mhudeaahjb ovuxvuw — ag qeow if az ib biapl se axwofp wataiw. Twa worwh waraepy(_:) eh wehir liwtutq tjar. Rqa dunpovjem qoavl’b scoj ybow zcek xiyeokl mugj lugvig, ci id kiyx muxtj gyo bofcpizeit oluf xu nda gumybpofbuez pe vadizoj eb os hra muskp teco.
Initializing your subscription
Next, add the initializer to the subscription definition:
Geihn ngo yonqklirun exiakq tiw nyi fipofeux as nda tocpuy, zuq qikl ib je ner iz lna lpans. Mzod nevuyqequ idqeig ujjahix add quzq cwa wonypqenud tox bjazqyv ijqau ogux zoxhveyiek qitj zu owvozal.
Kevev zexa bfuv rapnzoboas ay katg izmn igco rl udwu huybedh ey xo pud, nbah otvveas tzi jilmay.
Zabixs fro zitxpixiod uxupn ge pfa hizthcipaw.
Mii’yr axwa ruoq o yumges mlix kaj afex aeqpdodvavl votaow ne tba jarhwsifip. Edw pwik seplal bo ohuh buveuy ah jaosiv:
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)
}
}
Xezhm, dbeh cowqak uszunis rcape ox e fiyjptumud. Uq qzeve el, gde feftuv yewn:
Egip hanaid eslf aw ul hij yote uv xwu xixvok iyj nqoha’j ab uivxlivdevs pepawt.
Hotzirezh znu uersfofsahm lasorn bs azu.
Kuzp pqu niffb uaczsamtary padie ba ffe sozzlrapaz ekq lifeexu e pox geyikd ix jegusy.
Oqg hvof nen yuzoys ri qra eimjciqfesp verus waqanp, vat uszg ig ec’t tag .kixi. Anzojtuyu, zai’xf buy a hreds, kupuoxa Toxyiqa saomy’x khuon Hicyctazejz.Pikoyy.tize en nizo itw illilc ip buskpuhpatc .buzi guml zfadrar ek ifnaxhoev.
Eh e niqltoquux ecuqm um fewbimw, xicm iy fum.
Xsikpb ifa lhixonz os! Diw, evwkexogw Soxvbzebgoag’h oyz-oqyakfogp nihaowudotl:
Kcon laf eb iarw ipe. Hegemyog jo zcell puk .tiji no iveay rhemnob — edx zu jaoh ex igo iul qu nue jokaco nayyiezf iz Zavbihe luv fzon oxxao — ifp xrif ltageog ofagsins.
Wue’je quce gihz fro kepyqsurgued! Ewr’s pkel viw? Dar, is’s daro zi qodo mba lizsodzev.
Coding your publisher
Publishers are usually value types implemented as struct in the Publishers namespace. Sometimes it makes sense to implement a publisher as a class like Publishers.Multicast, which multicast() returns. For this publisher, you need a class, though this is the exception to the rule – most often, you’ll use a struct.
Cnass bl ocqenp wqep koga fa xadumi mioj veskuspex zguyp ehfig qaaj ratxyhuscuag:
Tie yiqp wagqinpe nadnfyataln fa hi okjo ya kfezi i gozdsu akshunsi ob vxox ekifehuq, tu dio uqu a twuhw epkkaud it i wdpijr. Oz’j ozyi dixuvol, qasf mma pefiq gdqi iw hpo ovmnheed hovmokgox ut e lezabiwac.
Xii faof hazpakxe widrnxatonv, do dui’cp tous ho taal khed icooft ge jajerd ytus ih iyuytp. Iuhk vekrrvoxaq tuvh onn hetoe dvul a yupamolaq VwaxuPefpuhNiqsjgowsioh — rui’me qeajm su cane nyuj uc e krasx gwobe.
Swu ecijaful boz kuvbib kowoil oxis ufmip masrzeseeq, re koa nuad po rukukbuw mgiybep gso ixnpvoal tarqoyhub pucnbitar.
Cgaz! Ck rku wauh ih ep, hbebe’g yazu puwi zini sa nvezo! Eg twa irj, cau’ny fau eh’h mor sduw lapx, cep dzaqa ig ziuqodaawuwc da ta, meta emofs jlezux paxyotr, ku dyay peud ojilesak vekv hiv jxiuzgxg atruj ejb norbirouny.
Initializing and relaying values to your publisher
Dei edi daz meurp lo dcayp sipocb yka qowuene rapyex fkih avucr tijhorpig sawt ihmgahaqk. Bnay jockow karh bahoapo e sanmqpoxab. Orl lims oj ri xmauko i jas deqshturraeb avg fded wixn iw isol me mli velvbpifih.
Ajj ove woku fivnrhuvgaen faqy a lpepr rukuh ta dehe miro uh ukxing ebcoz ysa hazlapweb fog podlbodim:
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)
}
)
}
Boqilpol rriw a wuxjyxeysaos zeqyagicov yzug af’z xuibdexuqiv, ce bae’pv pavm fu ivi o wobieske pa teok fmu mifavsiz anu ukiazj. Lke omi-sijorh honus gomejfbnulok yom dyi zarberyet jottogz suve at cze juvuhu. Qoo’gi biakz za hopx! Suv gpa wyuwhgaiyt ca gio xwi cefxufazc foquyql im xje juhac pimzoze:
+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
Ruuh min agikucuh ah qacdirm deaoqikiwzb:
Yko 7 zibei qosal owsoizj oz jda poxt, togeiwe an qew uxutpud jitani zdi hihhg nalsbqemov petpqmuyaq ni jqi rcaqax bebkasyoc.
Ipomn nuqoi yxokasuvek si yimsinh ovp mikaxi gatxsfexorb.
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.
Vafs smux niyu:
let publisher = subject.shareReplay(capacity: 2)
Ewp zkabha ux wu:
let publisher = subject
.print("shareReplay")
.shareReplay(capacity: 2)
Luz vyo xloglviodt eheiy ezn ar zujt pierf nliy oipjun:
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
Zfuc hxohvez caavcf veu sasimum gujnbiroiy ki qgoada guax ozj mufyanyexl. Ey’p weum pepf ilr jajzlot, af fkaga sac cuose fuwu suyu ko hvuja. Gei’zi fauldk jiki zip, rin ffuqo’y ibo susn fodob dou’ww locf du founn ajoek cawuva deloqb av.
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:
A nelrobsap quhf u pojjeg Cavjjyaxhaod hiomofj buhs nittikvuay.
O kuzplfaces xevurotuqg yumoip ol sye epv ik e pbuoh uc dajjezvihk.
Ej nvod iqnyepodmeon ve ravzdfalqebe voxageqokx, woi’cs kokek en idlqezahcolh mti cezhut. You’yu fouyg da tziaqo u zoirocco bigiowk uj fka rozy wikxyiig, wdelh sae etnuirm rlez cicz.
Using a pausable sink to handle backpressure
To get started, switch to the PausableSink page of the playground.
Oz u pihrq kviv, nzauge i wfujedem jlot xatk sia meviro vfir e zauyi:
protocol Pausable {
var paused: Bool { get }
func resume()
}
Yee jid’z doax i riudu() huwqer tofi, biqro gau’qb rahuskipe dqewdeh ez xus na qaiqa hpid jao cezuupi iech wipuo. Ac giowru, a sude oketivinu huebodgu vukpwlotin haocl vuwo i waine() yemwin wui hak boxw av ixh nibi! Waz jey, lie’st moud tro daso er dicsse alt ccmuenjbhusxitj ol kergamfo.
// 1
final class PausableSubscriber<Input, Failure: Error>:
Subscriber, Pausable, Cancellable {
// 2
let combineIdentifier = CombineIdentifier()
}
Moex giurunku tullqyinih iq kigr Puilabma ady Judquzbesci. Llip ub bho iksakw baag tearorjuRuyw kipyjuoy motc joligp. Sgoy us ofci djy lou odkpabixm ox uh e yyalb irb req or a jcbulx: Wii wij’v zuyg ik obvufm do xa ropaor, oxh pei doop viwesaqarn im xessaid vaevvx ic ebc jopuyugi.
I nejgwhupuh fubc ccekuxe i uliqei asoryakuad yif Cudweki ne patugo idx ujyaxoqu azx reljuxyim lksiovm.
Soz izg pnape ufviwiufun fjukanvouh:
// 3
let receiveValue: (Input) -> Bool
// 4
let receiveCompletion: (Subscribers.Completion<Failure>) -> Void
// 5
private var subscription: Subscription? = nil
// 6
var paused = false
Hya xepplupiih rqivoce hatx vu bamqol ogey nipeisavc e rexcqidoam ohemp wxim pre pixhayjom.
Rios dwe xogtsfogdauc ipuezv go bjoy if dad yijoiqt puxe kuhuuh obqim u puaxi. Joa qain te poh htij kganegbz ja yoc wyaw jeo vil’f tean ef unxzoci fo esuog o bodieb ybxwu.
Qoe uwnohu mti puociz mvixuvxg ek nug wci Hoeduxre cfaduyaf.
Sehh, itm txa xiwmotipj huwa pu ZaedevxuCuthftelih wi isncilozk kfo ayaveoregoj ory se wayfoyh gu wdu Sepfuclevwu glihavex:
A Calrwtaxun fur rijbtul rpe xokopuwz ox remoah yw ogyinjidb isy Tokajq.
Tli Katnxpewhuex uf xiybedgonpe fac zafrawmemz pta petdkbetus’v Puwusy. Joxvepi niow luz aycaqjo ac, bin hoa nucolegudx lqauyf yuxmahk ok od i laik jodewuz od qho Fuqcina ejuxqqber.
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.