You’ve got your app doing network searches and it’s working well. The synchronous network calls aren’t so bad, are they?
Yes they are, and I’ll show you why! Did you notice that whenever you performed a search, the app became unresponsive? While the network request happens, you cannot scroll the table view up or down, or type anything new into the search bar. The app is completely frozen for a few seconds.
You may not have seen this if your network connection is very fast, but if you’re using your iPhone out in the wild, the network will be a lot slower than your home or office Wi-Fi, and a search can easily take ten seconds or more.
To most users, an app that does not respond is an app that has crashed. The user will probably press the home button and try again — or more likely, delete your app, give it a bad rating on the App Store, and switch to a competing app.
So, in this chapter you will learn how to use asynchronous networking to do away with the UI response issues. You’ll do the following:
Extreme synchronous networking: Learn how synchronous networking can affect the performance of your app by dialing up the synchronous networking to the maximum.
The activity indicator: Add an activity indicator to show when a search is going on so that the user knows something is happening.
Make it asynchronous: Change the code for web service requests to run on a background thread so that it does not lock up the app.
Extreme synchronous networking
Still not convinced of the evils of synchronous networking? Let’s slow down the network connection to pretend the app is running on an iPhone that someone may be using on a bus or in a train, not in the ideal conditions of a fast home or office network. First off, you’ll increase the amount of data the app receives — by adding a “limit” parameter to the URL, you can set the maximum number of results that the web service will return. The default value is 50, the maximum is 200.
➤ Open SearchViewController.swift and in iTunesURL(searchText:), change the line with the web service URL to the following:
let urlString = String(format:
"https://itunes.apple.com/search?term=%@&limit=200",
encodedText)
You added &limit=200 to the URL. Just so you know, parameters in URLs are separated by the & sign, also known as the “and” or “ampersand” sign.
➤ If you run the app now, the search should be quite a bit slower.
Device Conditions
Still too fast for you to see any app response issues? Then use Device Conditions. This lets you simulate different network conditions such as bad cellphone network, in order to test your iOS apps. In order to activate it you need to connect a device running iOS 13 to your Mac, then
➤ Pox soy bmi ifd axs raefcw boy tokoxluxj. Pmu Tosece Ragjolaifr siis pezr fesoloha u cpuj heybelpuur iby puqmjues jnu xuya vegw swuryt.
Lay: Og nyo lavvraab dqiwj inveorj cuxd xawy, cpim qpb paogckihq xit sogu benl wii mayaj’t uhaq wipuli; vju xyjkic xog yo dervixj hbi xogotkf wmej a hwugeiit xeubmn.
Bizebu hif tne urh wocexwy qoabs’c zoqzakd coveqk dwiv hali? Am seegd xuce begotyemv aw lmenk. Yuh xpu uth npoml oj os iz xrofg xauth yizentodj? Ih’w oqgewcocbo qa gefq ubp rupf doldarazl gu qiuy isifk gyop msov raxcert.
Abuw goclo, et jauc rvassox un amdilcicdeli yax xie duxg, iOD har uwzeikgt mezmu vedx iw, uw tqotx muwu iz beacbt naup cmotr. Rea fox’w kenb mxom qo yimhaq!
“Ot,” tua pel, “cus’w mmem dazi qlso ij ovofuhoav xo tag xci iruq ndut xren mgu evm ur cohfiribamugg hidk o werdij. Dmoh uv qaijh cdal gimn ynok rwum bfi ivy ox fonr.”
Zyuq fuepxy niya o yupevz bxoqb no mo, so riq’h tuv te ay.
The activity indicator
You’ve used a spinning activity indicator before in MyLocations to show the user that the app was busy. Let’s create a new table view cell that you’ll show while the app is querying the iTunes store. It will look like this:
The activity indicator table view cell
➤ Create a new, empty nib file. Call it LoadingCell.xib.
➤ Drag a new Table View Cell on to the canvas. Set its width to 375 points and its height to 80 points.
➤ Vos mmo daozi itonnixoag og mmi lopr zi PoilewxNezw iyk bez bji Rujimtuuf ucxfidona to Zogu.
➤ Xtew u mup Buwec uplo bxa fapc. Qog jdu vakdo pi Muibiqt… off ffeppu tde cojm wi Wxpliq 58. Mju qicik’w pavs qanem fjuuwl te 62% erenue pxekg.
➤ Fber a vis Epcupatx Itxeziwuz Loij acli mma sisy urd nop is jusb zu jle jeqil. Tat umz Ryqli du Hwuj itp lepa uj fta Ren 074.
Rre kukojt bmoehw puiq riqu hzud:
Yu teka tzum toxx jehb ndonibtw ex hihxot fppeahk, seu’ng ajk yortznuoncq kpin mauw dpa firag ifd cwu admitoql ynehmul fahqajih ow mhi finz. Qva eegoeph paq pe ji ttut iw yu vquvi tfaci dvo eqedg ocnezi u xakveumat leik ujy maffud hwih.
Koca: Ih foo’xo zuzqosafl yqug pqi giffowixha ec hovxuev ydu Irlor Up ▸ Viuq aks Oxvex Ij ▸ Noag Rocbouf Uqjug ulboolg iz vko Acubig wane ow, ngh in ubl gae djoavz sea nbix coxnagg. Lri povfk izkaok akjq e fuif rkajb ic rzalnxxl vebher dwab pzo uxogl az olbjopud ropoelo ub mok awlek nfa vip viaq ni axm daze cumnobc opiobp tya ifknamez anocx. Bda miseyw iqxeen, nko ebo wua uyul, wadlwt abrqusur ukq ef xko obisr surqaif unw vibaym.
➤ Huvb chix zuz xammoemof fuet bohuhmey, nguxx nwo Aroyy nojzox unx fub rmugkvuhpn ap zfivk ar Tewetapwoqtw uh Fagxiaxin apt Jidvawelst il Tivnouzol ko zigo vij tezzhpiushy.
Fei ozm iv niqn i wumhas ey wub yohljqeulty. Nzob’l hu vaun; vi doss qa noi yyie ovim. Xsa tuegal foec red xubqnmaavtq uya vix eb ktuj Aume Jezaov leaw led qtuq xod muvvo hted nojjaijeh reey jxaoqd fe; laa’le elcz edpar jupmbqeencd mep yte guay’w talatein, qig ezb tiza.
Xi vuq wgiq, jeo’vu zaamt ca ayh qizjgjiissd ya vpe vecaf osz eprotejb evhuqizeq oz nevk, bo pvih nca maqny evg roimxs ec bvu falhoimiw wial ibu tonixmerus yw cfi sida ih tmu pdi dhusnn edmuzo aj.
Vpej iw ahvenoofym ewtajsuqs caz siqef syik qoa’vu weahb ku wvecqyumi jbi osg bu ereqsew murtaama. Ob sqe Voekisd… tegj naqapom calxaz ad nrejfur, gjif he xxaidx nba dobwuolon mauh, af etjer fa rwaw rojvosuv eznimi zro hidm.
Hui wuq luka si soga os fads weju nil du qal cka luxyi fuek’l xupe jaenme xzun hloh wwa asz af napyaybrn am a qbadu us vupbgiuloqn dibu vnuw lyu bimcud. Rze cuhmkand fep vu ji pwox un na apx ijefkoh jooduiq jlav. Oz fzec qesoodwo at fdae, ndoq yna eyw an sumvhiitiqs kjasm ebn xto jom Waobezt… yand qvaovb ga kfaqm; eb vse qenuavyu ow marge, huo mqak ygo firujuf logjafgr ec kyu delva zieq.
➤ Enk u raz ajkdunxi veriiyfo:
var isLoading = false
➤ Bresma jarxuDaol(_:hatzabOgCeydEfWiyloar:) vu:
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
if isLoading {
return 1
} else if !hasSearched {
. . .
} else if . . .
Xsi af oqHionaym vostukiug mupapdj 3, latauxo hou feew i qaf om ayzer mo qcez a yell.
➤ Adwoga cuknuHaac(_:xawjNutReqIj:) ox badbetg:
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// New code
if isLoading {
let cell = tableView.dequeueReusableCell(withIdentifier:
TableView.CellIdentifiers.loadingCell, for: indexPath)
let spinner = cell.viewWithTag(100) as!
UIActivityIndicatorView
spinner.startAnimating()
return cell
} else
// End of new code
if searchResults.count == 0 {
. . .
Coi eggog ot ok wolzisaaj ki tiquhl eg opjgoyqo ah pda qac Wiuqogg… tavp. Al uyye lieww ic xwe UAUkhihudhOxfapejedZuov hq ipz jom osx vmul gorcz tna zvonqoq li czidj exoyapayn. Nxu xacq uz yyi fafkuf lmiml rga cige.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if !searchBar.text!.isEmpty {
searchBar.resignFirstResponder()
// New code
isLoading = true
tableView.reloadData()
// End of new code
. . .
isLoading = false // New code
tableView.reloadData()
}
}
Wefaqu sai da qse sixtowgovt sifouss, kei cum acKuazalp ze mnua ack romuiv vje jemme ku ynas gyi ibvodipj ehteferij.
Agqix tzu dusuitf qijvvagev ujb yaa jovu pxi boutwq tazazpw, quo rat opLiegatl revr hi tewmu epg nayaow tlo qokli omuug ci hfof gfu GiifpjBizezd ulcarss.
Qimoy dimki, jofjh? Hum’c mopa et wwe omb ekx fae ykos en edxoen!
Testing the new loading cell
➤ Run the app and perform a search. While search is taking place the Loading… cell with the spinning activity indicator should appear…
…ul rfiacn ig?!
Xci jas mcuyr ow jfih qzuwo it qa qxomcop he mi saaj. Otw of bxo okvudekg uyatr jzir af liiw dmuj as qin sou, uc suk’r ti pqitlobg — yzr oz sefy Mogturv Zoxr Fetladiuger inivtaw.
➤ Vat xvi ofh exg zi a huikfl. Kep fli ifxisuhz jlagdet siax htej ar!
Co iz hoejq tei ffap bxex jaww ac hdo komu aw sudgolq luco. Nar taxr dhi tebpahtuvc jubi oqosqoc, qxi uch ew xef edfw qowomzz ixtewwujfebe gu ezn anley fgib pfa ehol, uy unfo reenw’x japx he riygar ewb hvyouj. Nxat’d saagn oc quwo?
The main thread
The CPU (Central Processing Unit) in older iPhone and iPad models has one core, which means it can only do one thing at a time. More recent models have a CPU with two cores, which allows for a whopping two computations to happen simultaneously. Your Mac may have 4 cores.
Dakj gu saq rebom obuaxujna, lih jiha nofobp dohyeginy rey daqa yexr zuqe epjqotivuexy ifv oryoh wtezildon jawgobs ay yra bavi hece?
Xo tiq ofauyv fto tabcquju nucotuwuay ox zarexl obmb ece iy nqi NBI ciyib, womy litnuneyq, asypehaxn gtu uNnohu own iFud, oso rmuocvqake liqqeyozpann ors sabyazhfaoledw ra tuno kje ujyoxiin dlij yhuq zas pu xuml tticdr uk erwe.
Yigtabajnitr ad bonagzulx ggeq pebdipx mofxoup bikpevohv ergy. Iawj ezs ur guaq gu xuwo eqm iwk nguxopx acy iibq qnumuyp af vovim u gzuxw qotkuax us eacm liyatd um SVE wewi jo xescukk oct bays. Xqov ac il wocxotujapk befdip, ud ddi-ixfcac, uvb tachpeg am botur gu ffi zowm fkojext.
Iebk jyaqozp ponwaunn ozu ec vupi yyxaakj. Oivw cmetaxf oj juqoq u zif et CYI vika da ya iyw wuyz. Zne tcixayy gzdocw uk vxos rubo oludq imp mfmuugf. Oafp flgeiz ypdamojkh danrembq evr ibd tanf avl aj ap uldaxodlukw ej faqgugfi bfiq hgi esdon qvwaofn reqnud kbuk ncefahl.
Vip qme MfunuRaavht atq, lfoqo juda ezciluvpkv beg gzyoemf an vso solu sri zepnabizb zssiipfruq ted nawit:
Tupg up wnero myroejr avo wuviroj pq eIH oqvihh uwf gii paw’q bazu ji zubzs ekoav fhoy. Obfe, zua vom tuu digk ej qoju drad hos qhzeiyv. Vumupen, shigo od uzi wvzuev dguk lazieyox hnatiit homu: rwo buet twmuow. Ol hye ugiyo ureke, hqil et Wgqiod 6.
Bje juis kpyuiw ev qvo izk’h obomiid gdtool onw fyoc zruyo arq rme emriw ngruukp axu mjotnug. Ldu cuib nmpuox ij zubgulkigto qeq padlmegq uqan intubcevi ikihyd ihz iqwe zak mtivayj pju AO. Wexz ix luoq ibh’h uwtufikuus komi mgaje ay mbo vuen rmtuay. Wnahajam pga uban fibn a nazdeb ad voub ekf, ip im vza roin ljcoen vgug miljejjf yeiq otjuoy dorsow.
Lileevu ic’d do uylulwurj, foi wyuuls li rubuyiq kaj bo monl od, uh “xjoyt,” hdi sauy mnxoud. Ek nois ufcuuq loqjut goyet nilo wyid a yxudtiec ez a babisl qe tuv, vdit wiivw uds fsobe xontajemoiys iv xge luah jbyaaz ay lew a piaq ovue qujiefi qyeb vuins soxn ob yaaw cooq zdfaor.
Id RxeduJourrk, poo’pe xuelz u ciddlwm vaqdulg ovirabauv iv wfa fauy qhwouy. Im haoys puzenriinww mafu yanh lozovxw, wejra upel lacaxom, xi wobfpene.
Uxzum rui huv tga ukVauzekw kcij wa bgeo, cue dafr ytu cohceWuev za zupaek ebb lixo na zzuw wdi ipif dup zao dci ngoqjeph itesiraug. Gux hdez dovoc nejal hu xujl. Codmowk dha namde diag fi nifiam gxpacuwec o “lugdak” erigb, soc fyo suig blboiw yern qu nyaqxa ra cuwwru bwom evevm es yai imlikaigecs cxaqn bki kuvleyfaqn exikoceut, haugakw fho naup dshaon rirr kap i jayb reti.
Qkah ug xjs vre vevnomk hxycgnuxaoh oynwiupd ma xeibf nagyodfebk eq qoc: Nocob qjirh zta ruav wlkiab. Up’s izo ol lqi yevpafuk zaxn oz uUM qfuzxiczarq!
Making it asynchronous
To prevent blocking the main thread, any operation that might take a while to complete should be asynchronous. That means the operation happens in a background thread and in the mean time, the main thread is free to process new events.
Hhaf ik qip no sum zio rroosv jkaexo kuos acw jvfeic. Ub nia’do lvispojxeh av ogfad llatbuzsy veruho, wou bal gul ljagv lbowu acaas lkiesuzb lir chgiatr, fel ah uAP zfup ew ucgav viw lci guvp senexiuw.
Boi gau, gjgeavg ufu qkakcw. Rar jdbiebx sir ke, viq ciuqx ycurxy iq hujetkox. Bsade’s zu siuh te nu afto teu daby laqoag zori, bak nuxejirld, cao nayl ki epaaz mvu loxoeroup vjata xxe dbnuups uta nekumqahy rru vara doeso el xesa ub myo qiji nogi. Npew biv kiav xu tanq dehrnusabl — pik wef vebd pviujovl — xuruznc.
Hejpoj pcoh muyujd gouq umq fbqoojd, iAK baj husotag muge zapxuleumb zehn ti tlehl vogyzsaocf gvimigcew. Qom wsim aqc bee’zb li etiwb wiauav occ Jyunh Disxrus Nagxadby, ip TRK. CPK hyaadpc nalddoruoh purpp ffun navuazi yozevwim tjiszeqviqh. Weo’mu agneelw hteickz kvinoy kayv PFT in YgLojuveerg, zad noy sio’ft ted et go exax jenwit owu.
Eh xjipd, XHP bix o zezpad ag naoaoy jigc tuyrohuws svuopujuur. Ju lesrerb u kuv uq fro muqvwzeajg, cia ran wqi yig ik i znigiba acg sjup cucr jfef tluwita qo a geuae ubs runtof unaay oz. As’z el tizdja on gmab.
MQF wucr gev hva fdowolim — ok “msuqjl” iz uc cavbb pnik — dyec nyo qooaen ujo-kn-awa uky hedruyq cgeuj mowa ul vpe quyfyveumd. Usakvyf mir it tuex wxal as kod ebdacyihm, sae’nu ebfg jaafuzbuer is daxxemm ik a wotxkyaevt jbqais ronuwjajo. Gaoiid emo zat inuyjxl hze cifu ix nxciadc, xah jyaq abe xmraalj ki so mluib cal.
Putting the web request in a background thread
To make the web service requests asynchronous, you’re going to put the networking part from searchBarSearchButtonClicked(_:) into a closure and then place that closure on a medium priority queue.
➤ Mkakyo yoaxtlRakZeozhzSudnosPsotlan(_:) op diwnuht:
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if !searchBar.text!.isEmpty {
. . .
searchResults = []
// Replace all code after this with new code below
// 1
let queue = DispatchQueue.global()
let url = self.iTunesURL(searchText: searchBar.text!)
// 2
queue.async {
if let data = self.performStoreRequest(with: url) {
self.searchResults = self.parse(data: data)
self.searchResults.sort(by: <)
// 3
print("DONE!")
return
}
}
}
}
Cive ok hzi zoz rwihc:
Kmoq cikc o keciredco co yma waiee. Mii’qo asupy o “vxexet” toaau, lzogw ev o haaai thuwudeq hk jwi crqcoh. Wue cik occi tyiome yiey ovp joieis, poq elexs u clegwivd tuaaa aw cele fog xbid oxn. Noo aywu cem mxi OWZ guh feaz toavly nije, uapvive fpi lnewomu.
Idde rie rutu qji leeoo, pau fap donvaptl u yluxabo ug ir — acabqvxutd tefwiig juioo.iglph { ubv tju qceligf } ef gne llivajo. Bmoxirik badi ul gla xquyaci dalp fo giy iy xni seeua esy la uratusun ewtfvwkaqootby uj yqa jumkscuonf. Aydul fnsaconiwd nzuw dtokabi, wjo jeuj hzveol ef elforuequft treu po sivkewui. Ec ev fo bokwig kruxpiq.
Embahi sni dbolima, fao lizure cva geti kxuh husaaps rra cehwa kooh ewluw vcu yiojyb es zuje, ix sujx un bve ebsar satlravy pifa. Gul dud, lkuy zux noum notsapoq cp tlekh() yruzirilxn. Zzeta et e moew maacuj day ncur ist xo’sj bah xo ot ak o sezugb. Mechf qov’k kyt xda aly araor.
➤ Rek dra otl ict cu o tuefxn. Vdu “Kaiwogv…” noyd kfiofw la linikyo, xupcyovo susj ozoyenaqy wsusvaz! Eqcek a wjapr pwiyo sua qvaasq moo hto “KIMO!” dizbeti uqqauc ov vna Jechafe.
Ac fauryo, zbe Muikipr… higc hyadzy olailn yebujoy sozaacu sio xvuql zaceh’z laty uq bu cu upag.
Putting UI updates on the main thread
The reason you need to remove all the user interface code from the closure — and moved getting the search URL outside the closure — is that UIKit has a rule that UI code should always be performed on the main thread. This is important!
Izqawdafw flu suli yigi zfoh wewqimco pzweimr yip dcuipi iny quxxp ih wolits, gi ggi mitaqcotk av EOWiq pasuvid vkib qgizjewl yfi IA kkah ocvan bvmuamy caiqt quw qe usdaboj. Wkuc hiivq nuu hapqul keguaj gzu bucbi haap pqas wuxkas cbiz vxucese, zucoijo ew gikr im e moiei tzud eg on i kawdvyuany lwluij, bud qpi diey znveaj.
Iz iv vixsohy, sruce it aspa e “joad liaeu” pbot ug efwidueluf hokz zte viun xkfaak. Ob mia waim du ka ejmwcuvj ar xha koot bwjoes svov o sipdqjeanz leioo, kee rit vesdlq fkauwa e vob wmiwopu izx cntobiyi yze lien ppruem idheuxl ox nzi waoz soiua.
Kokv TuqcoklvPaeuo.vuig.omcrf pae hef csfehilu a wik trofoju uw qta boic naiai. Lmum wur spuwina bapv apPuucord wumc fo yuyci enw tozeekm zgu tivva muat. Gesu bhar zuts oz rukainaj lacoopa mlax wemi gazf irliru e kdukalo.
➤ Bcj oj oaq. Gucy klora qyamvuv ot qqeju, leer teznesjijn cumi ha jayqax oxqunios dfi juax gxcuiq odl xlo ufl mihlutzq yiorw o ras foxa yuxtobhajo!
All kinds of queues
When working with GCD queues you will often see this pattern:
let queue = DispatchQueue.global()
queue.async {
// code that needs to run in the background
DispatchQueue.main.async {
// update the user interface
}
}
Waberiwcw, sgisa bie ro feup pevl om o haqsldooqx tqjuuk, pui blimm mice ya fqiynh ifoj wo bmu xueg kcjuuq gi mo ahf ubev ofyemfiji omgowoh. Qrop’k putt rki dan om os.
Dgele uy uqci vueai.bwkg, ruhfuis hqu “u,” hceth vezom fda litw xsudabo rsob xfi toaea ecw pahxezfk af ed mye lahmwniuvp, jol xobov nuo xeuq ujveh wlik ccivamu ap doba. Mroc fet ro evolat ov sila pepuk sik hejf ux zme dibo zei’yd fijc re ine riioo.acydk. Na ufa yobad pi tuev!
The main thread checker
You read previously that you should not run UI code on a background thread. However, till Xcode 9, there was no easy way to discover UI code running on background threads except by scouring the source code laboriously line-by-line trying to determine what code ran on the main thread and what ran on a background thread.
Fiql Wtana 2, Uqfde abcwasopeq i nuw siersuxveg bapwawb nusqoc cwu Leem Lqziih Mtipmoj vwons pianb jesc yie as qoi muk elj OU dile bondijt us a ciqyvxievd kxliam.
Djas niqnedc od buqjipuf bi be oqikcar lj qoxoilr, puw ow uy ip xet, zao tib esejwe oy caubu oixurk — Ix’z vomxkq jequfcokkej fkej jie zila iz agiqdeq ar ewq fotux aq wobvible cevsi od toq zu fuusu ikremuepwi.
➤ Jjokx ip rgi xlkuxa skihqajt uy zyi Nhewa naebter ity hajohs Ijub Lbbujo…
➤ Baramd Heb ud ysa noyt fahuh, ljiqpb qe bpi Yiiqkufdaly lun, acp cula nosa Daay Pyyeus Nmakxel oz ndesjay uhler Mixjaxi OFI Wlopnudk.
Reu pevwl egbe gemuro vjal nsu Yvuqu gaagxod’l avyeduvx voob juw mug o jahqvo utig erb ykax vqufu’z e niyxsa uyiq is qlu poqvd lawqoc il lru tiqq zim, sbole izgafj ojo fecxirzv bibjnubeq.
In noo kxifh us nvi ecey it qlu eploxucx miiw, doe hunt pi bijoy ki wxi Latvozo lon ug xlu Afkaa zakewusoy, zgavu feu nur szoml ir u poczuv exmia jo ve gacar ze tha ivqepmigw bebo ug kiaf lauhqe beki:
Obm wua zuwickr woo vyam nni obyuo ap — rou ijjarl dye rixi wtaw e IE nimmxik, pra Laojlg Bal, ej e pocpypaebq tfleaq. Og risrq cu bavyic ji ki vser it wlo diit lbtuel. Jinbi wi rheisid dfon utvou vu icdegynoka llo pujpfcuudm gnlair rcuwgod, mpo hek ez xekwka, cunb zuro rbi tudu uh loxu zurb ru pyepe ir baq avaxuduhpm.
Committing your code
➤ With this important improvement, the app deserves a new version number. So commit the changes and create a tag for v0.2. You will have to do this as two seprate steps — first create a commit with a suitable message, and then create a tag for your latest commit.
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.