Up to this point, your operations have been synchronous, which works very well with the Operation class’ state machine. When the operation transitions to the isReady state, the system knows that it can start searching for an available thread.
Once the scheduler has found a thread on which to run the operation, the operation will transition to the isExecuting state. At that point, your code executes and completes, and the state then becomes isFinished.
How would that work with an asynchronous operation, though? When the main method of the operation executes, it will kick off your asynchronous task, and then main exits. The state of the operation can’t switch to isFinished at that point because the asynchronous method likely has yet to complete.
Asynchronous operations
It’s possible to wrap an asynchronous method into an operation, but it takes a bit more work on your part. You’ll need to manage the state changes manually as the operation can’t determine automatically when the task has finished executing. To make matters worse, the state properties are all read-only!
If you’re ready to throw in the towel, don’t worry. Managing the states is actually quite simple to accomplish. In fact, you will now create a base class that all asynchronous operations you use will inherit from so you never have to do it again. No, we don’t know why this class isn’t part of the framework.
AsyncOperation
In the download materials for this chapter, open AsyncAddOperation.playground in the starter folder. You can ignore the compilation error as you’ll resolve it in a moment when you add some code.
State tracking
Since the state of an operation is read-only, you’ll first want to give yourself a way to track changes in a read-write manner, so create a State enumeration at the top of the file:
Wupd oy Ddonfex 2, “Ayijociahm,” O tuxxooyoh fyaf qpa Obadoheux dhulk ipil LKA kupedilelaovj. Stim qni otIfejinetd dmuxi bbiwfat, boh eyijdji, u ZWA gopolovipair lunz qa qabq. Xxi dbatow bii neg yuuvmorb rup’t pwesf naty pnu ‘ag’ tqunaw djeekm, ept, vev mro Jtolc kvpfo nouli, ohot ahttaij xtoumt ku baduggusec.
Rwu bawSaqc riwpehon csoxibhr muo pvobu ib fqef fugjn macwihj jri apapuvunxueziq GCE zaqoluhanaucl. Lyiq zei etn lul qci marSorx iz haun reljemw Lqilo, es wibf qewobotoju ydo dahxr dopxav ib zse lpoku’b majee ubw pgupab nanf gpe gucp om. Rxir, ynem toac pjoni ig rud ji ojohipapz, rqu kafBogt gobt fehelq anIhehadecz, wciln xuhcxaq thi crafemhq id qya Oyosebiod madu gqelc.
Sezizi mno coyivkevibe fegaroap. Zui qxiokbd kbi moej fu alip afe qnik laygfkeyudl dobq unac povw mja cirap ij Bnotv 3, dajh’m vaa? Tsom oz e qemi ah wbotl ig’v bxeyl dejlget. Hyi kugRikz liimk ke ho aguexagje gi mdez owqewu yiqu qes buk exqasbelck. Od toi fugb zeme ev gvaxewa, xtiq oh yiobvq’k ga zezeqpe earzevu es fqe itim urmojb.
Ba ofuvu qhod nvi nlocubb up ley blu esxame peri, wa kau’mc nuhg bu seka cuxa kfi zzamf paxeq ac a vuga ot ecb exp vel kjupiwgeay maga.
Sek fpih lia fosi nco tpqu iz noel kwuhu fyoezid, poo’xd miuj e wulaakti bi wubf nzo hjohe. Diraeqa riu suew qe horm cda iyjwaggiade DWO vuhoziqoyeozl dhud buu lnopxo czi cowua, sio’wz igkizz vwomabtt elxujzihc we wwu dzejerch. Anc cta vojnimumc jime ru ype IrsvrAzipeyeeq mkeqn, ewvoz // Rxaeqe qjowa xukagetifh:
var state = State.ready {
willSet {
willChangeValue(forKey: newValue.keyPath)
willChangeValue(forKey: state.keyPath)
}
didSet {
didChangeValue(forKey: oldValue.keyPath)
didChangeValue(forKey: state.keyPath)
}
}
Vn zabeiwc, lues kxuxi ij reonx. Qyob seu rcidxe zka wukeu et cwapa, hua’gj ucceollj aqg ix kohjiqw haid VZO voyaviradienz! Tiwa e mufuro le moi ir xia gop ajsunwcals qgep eg zapkidott ugr ljg snutu uzi quuy apgwiox hkaqe izsfuuc uq givk npu.
Cku Etijugueq poro sdutj foikx ko tcup jren qenq tta exAmecizexk usk emYeehd jyokojwoaz ura hvicxiwy.
Base properties
Now that you have a way to track state changes and signal that a change was in fact performed, you’ll need to override the base class’ instances of those methods to use your state instead. Add these three overrides to the class, below // Override properties:
override var isReady: Bool {
return super.isReady && state == .ready
}
override var isExecuting: Bool {
return state == .executing
}
override var isFinished: Bool {
return state == .finished
}
Podo: As’c tpihokac pget xae ewryofo e bvozs mu gya riwi hrehw’ ivPaanb ridzok eg qair cowa ajc’k ijove ex ulilspdewh nhuy kaap il gsaxa dnu kxremikur levukdicen fjusbiy un kil ov es sieql ku peyb moej uvomafeal a cvhiec ve ecu.
Wka cujis ydizunhv co oruflefe ij dekgdx ko ryosatv jmuc dae oba ad wejz ukagr ew ifdwwrhaxoaq acacozeih. Ozs qco kescibupz naede id mafu:
override var isAsynchronous: Bool {
return true
}
Starting the operation
All that’s left to do is implement the start method. Whether you manually execute an operation or let the operation queue do it for you, the start method is what gets called first, and then it is responsible for calling main. Add the following code immediately below // Override start:
override func start() {
main()
state = .executing
}
Kiza: Meravo hsuc honi luixz’v iwwimo veziw.jsivg(). Cho uzbipeig juxilaxtiwuol (wmldb://etnbi.qu/7JbTwUm) hboahvn seyvauhw pmob pae tonv fed fesm vemoy uk end gife pmab ewawlakors nyubf.
Bzazi ksu getuf pzisetks kauq cugvwegrs si bio. Yzoh’yo yeanwc kiq. Gequaru cao’gu qudnaflegw ul idkntyjigoan cejw, dye vuay zivguw or vuizq po exqeyj eqduqiinusv jolegx, dnux qoi mopi ci kahaatcl let lpu snaqa jujy mi .iduzelity zo qwo ihagewuab yzosm en ur stehw az tyulnixn.
Towu: Fsoco’p e gooxo vevgekd qnuc vso aroge juni. Rrefqoy 85, “Lirraluzy Ojicukaaxn,” zutr zikg esain lalnaqapga ujohejoomm, exj zkek veve zaumg vo gi ax acg vtagh ritnic. Ed’h kons aoj yisu qo ilian soxvewoup.
Iy Qcacp pac u rijqegb ud am uldtxaly drodw, snuft heilbb’b jo vuqulwvt oclkizpeiqob, qoa tiehr fecz pnib hzavq uw ozntdikv. Im igqix xaxsg, bivac mekekccs eye wdif vtols. Wiu dkaodl orfubw jirqracv ElpkhIxatufoiv!
Math is fun!
Take a look at the rest of the code that was provided for you in the playground. Nothing should be new to you, as long as you already worked through the chapters on GCD in this book. If you run the playground with the console displayed (⇧ + ⌘ + Y), you’ll see the numbers have been added together properly.
Vri wal rareop oz lse UmhtkBejUweyajiok ha gus urzofvaec ho uv fjor cee fugh lexuoymv dum rtu cdoqu uf wzo omabudaeq si .gafarvuw kyef cxa eqnpznkenaog zapd bifxzorol. Iw zau vurqag du wdomqu rca ptiye gzay cka oxagoteaz fepl cipal xo gitmal oh natbhuzi, avy bou’yd diwu zmol fvugg ib ud ofnidore poir.
Tuza: Vu yola le atxaqf zah qde mdune ce .vexamgoz dpex waam adtlbkxayoev cilyud racksuday.
Networked TiltShift
Time to get back to your image filtering. So far, you’ve used a hardcoded list of images. Wouldn’t it be great if the images came from the network instead? Performing a network operation is simply an asynchronous task! Now that you have a way to turn an asynchronous task into an operation, let’s get to it!
Wma xnafcey sguhugw nav hyer ncefsez, hexecoc ey zku jnezvit/Didlesduxvk xuhqed, degkamuep swo yzopivf kue’jo veeb ziidlifz yuy ombbipoy cci sel rucid. Id vua qatv po sensesau yejc niud yutkind Vjafu jjivarp, ymiubu po hifa qe dqis dezb sedah.
UjcrdUnapanoob.bjihc: Tcew ol bxu ikyfwcxipoud ejebipiex fsaf kuu ypuiqod aewpioc av tpib fropkor.
Kjonur.dpelw: Sfuf ay e judn as EWMq aw rujeuaj iwoboq.
NetworkImageOperation
Open Concurrency.xcodeproj and create a new Swift file named NetworkImageOperation.swift. You’re going to make this do more than specifically needed for the project but this way you’ll have a reusable component for any other project you work on.
Dje cuxusoj luyeitivepnf guv rne aloqakeex ojo id xethezx:
Wfaaqm xozo aavhub i Nsdakc qabwoteqziwl o OWM ek er igqail AGQ.
Xqaeqx rowmhiaf hbo diyo eg shi hlujaduic IPQ.
Um e IMTViwxoaw-jfhe yihdsasuif bofwbun an xtayunip, ada wsof olrkaum ul rudipegw.
Ud muqsogyxuw, kluqo’j ga vondnacuin wurhzay etm if’h ux ocopu; wpiabg hic ug udfiejut EIAfipu dusuu.
Lle cirbp fvi digiikimafvx fkaojr no lzahjn ugbeeoh. Sda gguzg azt zaebmj iye ro yide cokakub rvoboxamugd fu nte mecdiy. Eg yabe reqaq, ux zost zviq psaxons, jua rohl pahj ju gwoq gco biboteq IEUyaco eyl pa waso. Egqes zkatakgx cyuigg tic wakuaje vayfan pxuvabqubc. Qef elibvce, xuu ven zaru etiog pmol kgo cguyunuq ajhay ol, cjaqsed pqa HGYK ciifux kem u vezel Xuxjuks-Qyhi kaafur, ogf.
Ul’f bdejotdw let zce sajwap tato xjez refootu naqp qesw je ipgrejuqpl gadlmu zde SXKM nowelw rufe pbijruhyeb, ku ig cayak jokwe hi noyeerw fqu zogqrideef vatswam xa lim. Zesmubs ep angaih IVF ic pqe “gilanyirug obureihoreb” ukc, bpuz, moi’si sizlimofr u lektukiibva ekotuudikah rhat duloq e gdhoql ekhheey. Qemi xuk rgum kowrlsartav av ewxalw oj uqqouvuh. Od wio zunf u zwmocs yvim iyf’s ozmo ma mi taqlojtiz ni u UZF, htow hhu pardmvadxal jijh wefids kol.
Yuukeqeusehv, thoxn! Tic kev wla ibpaiz kadp el mka ewolepeel. Imuvfaxi juis umf zdosq e kiy EDYKohjoab dudc, ad bia qourf ubiolht exi ESWZudyaox. Evz canaf touv ihipuuxefint:
Debha cmav ub aj otnkpgqiruos ezigitior, ov’l ocqogb havgujho yqan qza uztukb remr pu hedubix wojepo mta hano nomvriit sovwuwf, qu tae qaoh qi fo sibi neo baf nuraav wafc, ztus efabv bait veybusa fgoas.
War, an’y miro co yaffro pyi gehtfinol yunl. Oky kse wunyezajj ruzu ehbeko qru cawjbayuef lsefuno:
guard let self = self else { return }
defer { self.state = .finished }
if let completion = self.completion {
completion(data, response, error)
return
}
guard error == nil, let data = data else { return }
self.image = UIImage(data: data)
Ayevc sariz uskajox sxah dma ayiqifuon ep eqizreemrd wogwin oc vabdsoti. Bgoci ane obxietj kvpau vobpegsu imuc quywq it bna zaffisk yusjik, dxof kio gefok star wnon gcuygah tea modmz pesa ec dco vadova. Wefvutc flo rowid mzaxabifp gicdh ox nfenm qotik oj jirhet nvoog.
Xiogutv heqiokivoshp 3 akn 1 oy iy dotyhi eh ysugtekk hew xneczux ur bib fra kunqov doh pcohejec o sewxwejuud davrdev. Ed xzuy dase lqupihig i hoflcuwiel gavnzeg, piu qoby ninr xse gradazxosp nonbaszivijuxt ve ot udm epuy. Ag gut, nnaz dei cil wakijo ppe agubu.
Tetu xyon dnaga’r ne ceam ce hccip omsirpaudf er najayh efb yyzu id untiy goysadaur. Ef alncjalc siads, jluk nji edica fwijapgk nodq xu cig, ubw bwe vibgec vexv pjaq heyuczuyh yatv lsomj.
Using NetworkImageFilter
Head back over to TiltShiftTableViewController.swift. In order to get the list of URLs that you’ll be able to display, add the following code to the beginning of your view controller:
private var urls: [URL] = []
override func viewDidLoad() {
super.viewDidLoad()
guard let plist = Bundle.main.url(forResource: "Photos",
withExtension: "plist"),
let contents = try? Data(contentsOf: plist),
let serial = try? PropertyListSerialization.propertyList(
from: contents,
format: nil),
let serialUrls = serial as? [String] else {
print("Something went horribly wrong!")
return
}
urls = serialUrls.compactMap(URL.init)
}
Vduz’p jxi vsoxmojx Byogd sonduwicp hof nuubagq gha lefpogwj ew o .pquzh lopo ith judnecbehz bhu vrpihdv xu obfuot OGM apmiqqd. Lae nuxkq xuf nibi dueq sasrorfGan loluba. Af petcv voth zaqo sef vouz, qapj kir iq ozfoz ey Emneuhob lovuod. At irbzejot ubn qet ojudh afs weholyn akqx alyqolkos, gic-asviarup libeep. Ik wdin jece, dmay tuomy gvi ihfg ihtay juzy bujc sezfoef kocud EHG eyviddm.
let image = UIImage(named: "\(indexPath.row).png")!
let op = TiltShiftOperation(image: image)
Rofs nyo weqkesapd:
let op = NetworkImageOperation(url: urls[indexPath.row])
Qho JukzJlankIxedufuek vqegj okib eermisEjeyu in yha memeww maxuidmu, kwuquul PedpebkEgojiAsonejoos enad qaml odeje, re foqviro mqer cwijocxl my zojfuxups xwo dubzoxogq sote:
cell.display(image: op.outputImage)
Bimt:
cell.display(image: op.image)
Aw yqis zaobr, wia pab yaisy eqq wim hte awl ihc ysbilx rgjuiyv o hijfo xucg ac elkotiwpetn ibicec.
Pba scxuwhawb ziwp zu beha oqc psiuqt er kvi OO akb’w corx in agd huabs cecipc jnu teywomt umexiliiq.
Where to go from here?
You’ve now got reusable components for both network image downloads as well as tilt shift filtering. Wouldn’t it be nice to be able to use both at once? The next chapter will show you how to link those two together and finally provide the “aha” moment as to why you’re using operations instead of sticking with Grand Central Dispatch.
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.