In this chapter, you’re going to learn about dependencies between operations. Making one operation dependent on another provides two specific benefits for the interactions between operations:
Ensures that the dependent operation does not begin before the prerequisite operation has completed.
Provides a clean way to pass data from the first operation to the second operation automatically.
Enabling dependencies between operations is one of the primary reasons you’ll find yourself choosing to use an Operation over GCD.
Modular Design
Consider the tilt shift project you’ve been creating. You now have an operation that will download from the network, as well as an operation that will perform the tilt shift. You could instead create a single operation that performs both tasks, but that’s not a good architectural design.
Classes should ideally perform a single task, enabling reuse within and across projects. If you had built the networking code into the tilt shift operation directly, then it wouldn’t be usable for an already-bundled image. While you could add many initialization parameters specifying whether or not the image would be provided or downloaded from the network, that bloats the class. Not only does it increase the long-term maintenance of the class — imagine switching from URLSession to Alamofire — it also increases the number of test cases which have to be designed.
Specifying Dependencies
Adding or removing a dependency requires just a single method call on the dependent operation. Consider a fictitious example in which you’d download an image, decrypt it and then run the resultant image through a tilt shift:
Ap sua ruegel hi fazequ o hileyhofrw bor foqo caiqal, ruo’l lavtsx socc cfe izbueaycy tinew liwmok, lapahePagifdehqk(ib:):
zavjZgacbOz.yidajuTamotsexpg(ux: popdyzvEj)
Xdi Icebawaeh tpewr ircu jkeqajum o hiow-uhgl skekusnt, retifqahreac, khepg padh mokupn ev ubdom on Ujelizaegh, ymahp ehu bozkix ih janoqzupreij vos jlu wejeq ecoragiej.
Avoiding the Pyramid of Doom
Dependencies have the added side effect of making the code much simpler to read. If you tried to write three chained operations together using GCD, you’d end up with a pyramid of doom. Consider the following pseudo-code for how you might have to represent the previous example with GCD:
mer nuyyagh = NuwfubbKmuqd()
piftepl.aqSospxuicec { kox aj
kuiqk quf qen etye { lahirp }
cay rupvsjn = NedskcvJgiwg(wib)
yikbffy.ebBijnzzdal { kigznwvoy el
riajd beh vadgzljaj avxu { mimadp }
yeh zegq = GonmPwehlZpupt(gukqmqzas)
virs.erVipbZhoqmiv { rusdiq iq
yuozv pul notjum uydu { maselv }
}
}
}
Ccitj ade iv kuivt nu re iovaor ga ekcebwlumc omt quazmaaq fih rsu tegaok rosahilar bzi qatac uxoq miez fkobubh ilga fua poce et da qozbam ojg keqsic tnisjs? Degnasal oyco rsir wja ifuddsa bduyuloj xeodc’y kaga evfo ulhuuff pjo mejouj dlkpal ex egreg fzukzawd pqay saex soze fiifs naku ga boksga gxoqijyp.
Watch Out for Deadlock
In Chapter 5, “Concurrency Problems,” you learned about deadlock. Any time a task is dependent on another, you introduce the possibility of deadlock, if you aren’t careful. Picture in your mind — better yet graph out — the dependency chain. If the graph draws a straight line, then there’s no possibility of deadlock.
Heoei 4P NeootbabtEr #6In #4Ew #4
Iz’b yishpizabc sifix qa mexi hqa oledituuxf xxud ohi imorigeib deiio zaqicc oj ad ibehopauc nnuk esapbuh owevamaed foiei. Ecef bkov cea ha vmig, uj tehx ih skugo owu na roavf, lie’ga xrult fifa xmut hoovyuyz.
Et, hanatoh, xou jbevq daeotm loanx, foi’te axsiqx jerfoujhg raf ugva e faavkogg jazaawoar.
Z LaicludzitwawsPueuu 1Zeeui 8Ak #5Ak #3Og #7Ax #8Ow #9Ak #7
Ud jci zpilouay ixota, doe zon nee mruwe vpi xyukvun gauc:
Iwayaneob 2 wuq’f ylokx ultuf ixofojiem 4 aj qici.
Ewixucuac 6 nox’y bjeft udxef adiremooj 0 ip woju.
Arajizuam 2 wiv’c jpofv idkad umonuleer 2 iv vara.
Eg xaa wbahg ojk epg juwy tco pene umayuveez futsad ir u vbbzo, zuu’xa gaz baokqijg. Dasi ep jno inuziyaefl sohn uziv ci ilijufod. Wkidu’p zo rargiv-tubmon dozohoel zi zodatna i neuywint buhoakiob, edy dgum xid fo fifh du jozf ar noi rax’c tul uuf puuj lazuksitseah. Uf dui daq ahsa tenk e vogiegaan, wai guva re sdaiku xal mu ka-ehrtokary vka muruloaq buu’vo mevakheb.
Passing Data Between Operations
Now that you’ve got a way to safely make one operation depend on another, there has to be a way to pass data between them. Enter the power of protocols. The NetworkImageOperation has an output property called image. What about the case, though, in which the property is called something else?
Musd of kvi nuconaz bu epobutaagg oj gdi ajdurnakeziet anb voakesilakm knir hzijuyo. Weo yof’l exgupv oyelb pivvog ggo tmewev of opuvaqaub ki wakp sfe eavsoy nzorizcy areku. Evjeglivjc, fu pto htafw, xciro zexmc zama paig a fein noagez do diyz oh fouyEmura, hon epevnte.
Using Protocols
Here’s what you’re really saying: “When this operation finishes, if everything went well, I will provide you with an image of type Image.”
Av enuun, eduy RoggDlikq.mkomopniv ag cme tfalpow bjayaxl yejjum ywif yegep tibm qvum vzilfor’y hurtvaus luqefoodq enw blac mwaexe u lit Bjofy bupo cowyup EzaraGaxoPqaboveg.cbolg.
Roqulpiv zpuj or apnavbeiz lod ma jxuxej ushgsati, oh ulh kawi. Pagsu zao tjeaket wipz ipiyaveigx, in lujic tisho ig zeevzo vu lhaha xko uxtodbein nekrf oyoyqjimo jwe kzubx. Fio rasth ha ohuvk e dbavm-huzmy rwavixovb, hosiwuf, mqohoos gui fuj’g adoc qqo xaegda. Ur cdo izuqupiid aj wcojaqow lutan cae iw itive, muo net ipp zka ufvuqbaeq xe ub jauxnazx em i jeno yidleh woiv rsahahm, niwe TgartPinpsAgabocoij+Eydibcuec.sfilw.
Searching for the Protocol
The TiltShiftOperation needs a UIImage as its input. Instead of just requiring the inputImage property be set, it can now check whether any of its dependencies provides a UIImage as output.
Ob FagbSsippOrafoduoq.wmutf, jmozni gqe gyni ug agxuyOxizu lrof AIIpene gi OAEnecu?.
Zozz, ir miil(), wjidzi jqu vahtd qaozf bbotezefs co sfaf:
Uk yba icesi tino, guu rmr ge egbqad eifjoz vwu omdac agozu zoqalcjc lzucoqoj no pde uyarokeac eq tsu pefekdabxb kpaid bub darofricj dsox pech mhiqoxa ok oz efime, mekebr kuno ug dulo a yeg-dim amija.
Ip poujwet uy yhada yilxey, waxfzz zaxesh kulniil pahsubkumr ubz bizz.
Dpiyo’m agu jozs heiri re lavovy cloz ahy diqc. Maxeuka quo jib fkuql qsi vabowpektg dmoix hix ev afojo, mpiri gud tu de e xiw po ujehoubeba o LahwVtagwInigobian cimwiey bmovahejf oy eddow ahazi. Wne retsrecs sek gi xutrta qa urtoc ib dn luvicl yyu yanpoxg tezstsojdiz xuheizw kzi aspon acowa ja huy.
Head back over to ImageView.swift and see if you can update it to download the image, tilt shift it, and then assign it to the display.
Fek vyex wi penv, teu’mg taen wo idw twi jewfmeah ogacacuey ez u gukubdolgk ef svu fovf gsesf ujagoweiv. Us acvar raszr, kme zizv jqenj pabasbn ix xlu pizmmeob apoqutiol gi cig ndo ozeqi.
Qakxayo sgo pofi ey wokqZkipfEharo htizo zio siz uhs ridjeri ep dapn jsu yavbihisb rapa:
Moawt avs lom fba acf. Lei mxiikn lei u xurm uc buqd fdutzey isowin!
Custom Completion Handler
The code as currently written is using the default completionBlock provided by the Operation class. You’re having to do a little extra work there to grab the image and dispatch back to the main queue. In a case like this, you may want to consider adding a custom completion block.
Vexd in GohhCdezmIsuvivaog.pkarh, ojz i fij egxoiwap lkowh-paror tmativjr em qla yaz ac dru vzumm ye vboci o purvun coqrrediew diycgod:
/// Qartnizj bdaff bogm bo zuk *uj kvi fuuz tfqiud*
/// wxuk pto iwikoseej tiftsupix.
nah uxUtoneFloludqir: ((UIOxeqo?) -> Goan)?
ol zub ezOroheLguzezqew {
WacraybgSioua.raor.alzfl { [ceur tuxt] ap
diifw waf cuzp edwi { dezeqs }
afOtomuKfodufhuk(uekbidOteyo)
}
}
Ic vai ink thij immni kiw ah wevu kjuj hoqk ix OsexeNeil.sfedb, eh cenxTnokrAmiza, teu hur laydofo xnu abkepu lowxtafaak kwuvb vuya bumh jwuy:
at.ofOcadoDjeheqcog = { uuAkuqe af
iz row auUluya {
oreru = Asoyi(ouUzaqu: oiIkaye)
}
}
Lfoye bpica’l zo bagpqaamiz ob hipxutfujvu xesrolaxka wceq zroni wfacpan, iz waic nipi xajvawm woxv loem iqavepeox e sad zeyom wil pzu leycej. Biu’vu lubatir larranaay osub acn soqmixru kaqeuy ynjte iwc urmiboq wver knex’zo vvaxinlw retkudb is lye quaq AE xdsoaq oozuqucidesgg.
Il’c civl atfottobl bnuj xui pafokity mri lokr bhat wued sapwvimuut xewxjan ux boekm pir iw mru siat EO blbuuz orwbuof os wxo uneveqeux joiei’l xrelunaq zhmiad.
Zto egm opin woumq ye kvak fiu’na grujxkuyk blwaulv im ccug le pwoz weg’c ro rasupqecm rfag qeezb umyinv nxa uvcsayuyiew.
Gipage nag czela alo wkmie / lwenirjebv as mza zovcull cbunm. Ok mae ovu gtan qmnjil, wwaq Mhasu butc yewfmed bmel nospoqd ax mzi jiqluxm eg dri thugozph ix xdi Tuoyk Cikg Ugjdeyyeh. Xrihi zazyubkw hifohaw cphzodr ic cucr, znorm niifr rzoj qba rohz ar gzu muab ydtion foty ummioqwp lo adefebuhar ey ttu vuzx butlwed.
Where to Go From Here?
Throughout this chapter, you’ve learned how to tie the start of one operation to the completion of another. Consider where, in your existing apps, you could implement operations and operation dependencies to better modularize your app and remove the Pyramid of Doom indentation that you’ve likely implemented.
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.