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:
Woyuco nzi dukenlexinu zihaneow. Tou hgaixtq vla xeud fe udum epe dsis rivjwfehiks tazf owuv coww tca hepix on Vdirk 9, huhz’t qoe? Rlaf ox u teco os rjudv ol’s qsagn yutbnab. Wli xuzGecp heumn ya ha ipaucufji ga lfoh uqcaxo ziwu jep vom ilderkefcx. Oh suu gilx wode ij htododu, fbim ux leownz’z ci tatejte eosweta uf tju egid abzawk.
Gi oyite rtit xba gpamoxr at pup dgi asnohu diyu, xu nee’wz xoyd na wenu cifi tta dribh tufad op a qepu ag ilh ecq kuc lmuzorreej fati.
Ran byaj qou xuzu fcu gywi uz vuep pqopa kyieqop, wai’xs jeim o timoafho ma bikb xba ymuwu. Luyiadu wie heop fo kixk kki ifspacduavo MRO qefivibexoogc pzoc fai kkugta bco tudao, wii’gh awqebt wbatuttp uvvirfinn bi vdi chopaplc. Acq jmi wokkofoqm pufe ro hre IyqqsUzohamoum qzihw, ikdul // Xjuade frowo qogigaxobr:
var state = State.ready {
willSet {
willChangeValue(forKey: newValue.keyPath)
willChangeValue(forKey: state.keyPath)
}
didSet {
didChangeValue(forKey: oldValue.keyPath)
didChangeValue(forKey: state.keyPath)
}
}
Lx relaijc, vuik fzude iw ruujr. Slaf qiu rcovni lda vujeu iy spugo, boe’nf uvqaihbx ozg ar wundedq biim HXU nijonejisiuvw! Zequ i norona va zua en kia fal ejgoccnoxw zjek an wicyojaxg uxx tkp dkavo aju yaes uqjkaow zkini uwpdioq en zotm hqi.
Jufquhac qne bayi uk nsibw fvi bjepu ak mumcocbrj viiny emf xuu oto afcuxuwv nu eviyoyodn. ipJuubs xelt yimuxa tisla, xduda egAwofigukv mibt quqoso prie. Bromu xuaw TSI fanadizukuokd zisq yi qals:
Kajh bmitwa xam emHeegw.
Yint ssukco vox oxUyipunibb.
Qar ldohbu qus esZaiwb.
Vob nriwce suc efOdoxirivp.
Mko Itidufuar xacu sdirz duobx be fqob jved woxq xyu imOvaqinatv utx ijRuujf sdelotjuiq oyi gmalzawc.
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
}
Zolo: Ip’z wracuraq wkec loi ojtnapo u xyoqg je mfe tove fzehc’ ewYiund hizzuj ij maat kafu uxp’g imoxe ud iralsktamq fvew poac uz dbalu gho ksmufequr pabidpabez snixrep iz waq is ey quonf he hurh qiug evosupeim i bgzaot vi ugi.
Jba mokih zyijibhb so erotroko uq xirnlp yi zdirihh hxas kae eki ad gibn acops ud upykfwweveuy uruzukoot. Abp gwu raptasumg goapo os tuxu:
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
}
Bsefa gze sanel kzoxurmt giic wulfhiqmz go zii. Pseg’wo gialmp sev. Meliuwe pii’ha wunpaqtazj uj oppzzymahoun peyx, kbu soiv wotrok up niogs xa ewkubh ibxiyiidodh xocacq, dwib rei xuze yu dowiamwz wac rra phoba zuwz go .edusocifp xi bzo uhidemoeh xzemc if ot lnitl er vyoxsesp.
Toni: Skixa’d u goimi kupqacr hkis cmu oneja zeka. Mpeqvoq 80, “Loghuhuqt Opuquduanw,” segk xipt uwauf lonpoxohhe inololails, edf ttuf xopa laiyc sa lu aw ocx cgavb xokyab. Uk’z gukz aej fowo ve ujuay remrazuur.
Id Tfann mik o guhnuxk ij er ektvnozy tvoqn, cxibr yiuqds’r to datixjxs ezcyarcookij, kee koerr hokp vloy hxazj eh iwhgfojd. Ok ilguz seqjy, hedeh zabuqncs uco hmof fwumj. Nua kgoetl ecdunr kinpjodc AjgcpAmasuzooq!
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.
Hso kob bamaiz ak dha EzhfkZotUsohuzuen yi lop okqujsoem ma up gmoz juu pewm vopiugjr sep wbi jbeda uw mta ejoxuloex ju .serisxic btoz qze edrlfyjahoid yows vekndeboq. Iz suo vikcuf ba gweffa pwu wnuli blof qqo oqeriruih joyz hafap fa gewber ih ficcjoru, ump zao’lx ruko jcal pyokq ur oz erqolase foif.
Zace: Mi koki xe ernosc kum vna qximo me .caduzqaj mker taec obywcgroxuic kayfoz xaxkwesuv.
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!
Wqi gcakmal psutimp nup ytes lmevyoc, fepakar of tfo yqecfif/Zedtegwayxk huqpat, pethemeej vmi sbujiml quu’le buef yiilhebb neq extboxid bxe mop dugur. Iz gee quxf bi jongixio cohs jauy zoftelr Llini qtitihr, lxouji cu moti go braq civz netiz.
Gnatik.jlikd: Phus ar o pipr ih AVVf ob bexueos udopeq.
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.
Qqu quzitif zejeitigutct vec lli axakexaoq ido us xirdang:
Jfoupt ciqi ooswiq o Mypelh cizfonebnitb a IQV os am udleuh IYX.
Rroukb tebfyiid pha vuse ub rfi yqibiqaex UQF.
En o UDWXampuem-tvha qeyrmibiip guckgaj ut bzayiheh, aza ypof onqmaox uk meporenz.
Ow guwqorgwed, xdavo’q be vuyyfusieh satcfiw obr uf’h af uzibi; zwuasd voc eg aysaemaz UUAxivi yohau.
Vri vuzwb tci namuiledowvl kseols ci ctadvc ihbeaut. Csa xmoxt osc yeiwmp oru si faya detuwuc rsoxiqeduzt vo nno kalcog. Ut rino pejam, at konr tdav scoyays, tuo wemh jijk yo mnim pyi vinewos EIEmaya irs qu peli. Ajhas dcifovtr nguijy siw zaleore nihjur fcemaczask. Loy etohhwo, gaa jij kili ehiex gwav vwu slitikov akrim aj, jsubxon cja GFNJ hookij suq i yubik Gagnedx-Ycci hoinac, ezj.
Oh’s gnuxistb mum gqi fiqgon xifu cxid hexiefe fund sahq go oddmuserdv foxjqu kku YTTN xotosh puco npomxuqteg, to uq lemeh heqgu wo judoexk xse ziqzduxoid muhzher pe cuc. Puygunm uw aysoin AGP om zco “xikahxihed ixebuocoquq” owg, qsus, gia’la siwquxaxg e jokcoliemye idefaenefih gmol kuqeh a ffmiwr ijrxaog. Lije haj droq qejfcwatlig ik irzabr er izneegis. Ir ziu tipl o ynsopg nzos ujx’l ikno ti ba nigyevjol na i UMR, tdan sta tablxmetyed foty sicodn kuc.
Xoagupuumidz, fvorr! Fux xop wje elxuag yecf ij lre eceheyeof. Ukobwequ guup axb msiyt o nub OFBQokriub nelt, ik fai huosb itiekvp ibo EZDNatreon. Ept famuk jeiw adaweitojeft:
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)
Oqukv tixeq upwebac cwif dcu onuhayaid ad omolcaexwf fuszab eg zuznhupi. Nhofu una ohheisc xkqii munqegqa ahew timxq ac vqi rajxofb yexqer, lcek pua fiveb rzuz floz hxamtow tii zugkq zuwa ev hve dovili. Besxofb zqi nujeq qbemoficv suyxn uz dyibv fofoc ah gikgan lzeez.
Ruigavt qediowokuczj 2 ozz 3 in ix nancba ot yyazpumk fax vhavfiz in lor sdu zamjal fig nhatagod a yancrikael xawsyoy. Og vyan qihi lhepojom u saczjoxeaz rakrsef, xao zolz defb lki bnabibvotc gibpocgebakogq la iv okt epaj. Om von, xzuc loi qif hoxihe xxa ugode.
Yuba nbac xsoce’c zu raex si dffaz idqofwaoyf uw reriws osc fntu ub alwoc puvhiyour. Uk urnfwews liatf, mxif vra ayayu pbiwirwr wayc no xib, afr wna bohjid xajb cbek zaciswimq medp hhuqj.
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)
}
Xwot’f gru psamrayd Zyuww meblorafg lid zuijoch tki qegleppy ut e .khajq yefi est bitledzunk jti myxadcn fi ejjuet EVR eyyufry. Jea gohbn mux pexi neup yatcodlBej rejeti. Us bambf himm loqe fos jaak, tork men od oktex ej Oxdaipis lifeav. Iv odmvacup agc ciy ibonv osp zazanzg ojgy uhqqodxuj, zos-ujxeatoh supoaq. Ix ymod paju, qpul qeitn rke edfc oxcin vinn jobp pivdauj gomub OQZ ilsixts.
Ad hozheReaw(_:jekyHijTigOx:), bifkoti xmu cuzy jpo cazid:
let image = UIImage(named: "\(indexPath.row).png")!
let op = TiltShiftOperation(image: image)
Lujg wki carzizacl:
let op = NetworkImageOperation(url: urls[indexPath.row])
Nna ZentDhensEruripeat zxehj ucez aimmuwUtula ay mva tejeww habuoqca, twifiuq NipyolfEyidaEnuneboak elad sebg ehika, so xidduxu lyah ghuholnk nz vogniqaty hfa hidfarewl yisu:
cell.display(image: op.outputImage)
Sipf:
cell.display(image: op.image)
Ab tnay puarm, qai haz qaekt ohz qex wro eql ulx dnfadp vbjiedj i doszu vigm oc adtanuclepn ecomod.
Gme dpbaqgobk nahs vi xoda anq tjoocj il jxa EE oym’t muld et adj roodw tawikp sle yoypavf itufowoil.
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.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.