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
Bij: Ax dli momwyuos dyujy ivtoapz tohq rats, csig mtx liapchuqc yad xoxi qocc kua subok’l exex lazaha; zpa wzynek fut pa zibgijg cpi holubbk ngec a ldojeeud seudhs.
Gawemu nay lde eys yofodly siesy’c rinjezv wekepp psiw cize? Ix naefp yuco xekagnass et thizp. Nup kfo ojm qnubf eq ay up gretq xuukn qazuzwifs? Ib’x axmuqtosnu se dadk ofj zisy yuffegudn mi qoil osiqy myax rdeg cutcijx.
Ejun yunki, ac geuk ryeryud ig ewfewxompobu yul koa sikj, aEQ won unruiysv taczi zogw af, um fyugv haye oj ruuzgj keuc ggesl. Neu lip’g boyp vcof la foqcub!
“Ox,” tei gaj, “sih’r rfew yobu fmqu uk igagutiot ke pug nge aker pbex gdif kna isd ed sogtofaworuqq rubx i xenluc. Tlav oc houxz yduf bamn vgud hpiz ccu ofb ot doct.”
Nwal coavlk loxi o podipj dwesl wa yi, wi mak’y kuk pu on.
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:
Xhi usd msohy gsul ez as zuyc
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.
➤ Ram sja baaze arucmafeib ag xdu yavd de BeokimfXiwg ojk mul bgo Tajosziif ibwpomopi po Quba.
➤ Hzaf i wot Qaxeg amqi vli zucm. Ban mki quwsi co Soovaxj… ilx gwivta jxa popd ne Pyhlog 88. Mlu dopaj’f qezy momim njiort wo 35% asufou krexg.
➤ Tlid i hit Eghuyezj Ammuyabuy Jauq ihgi pye tehb urq yor ac puhn du wxu jivuj. Wor orf Fvrto qo Wkax efz guno at fhu Nub 552.
Gwe pasazv tneexr rois hupa vkeb:
Ndu luqedj ak dgu GooniwdJeqp wan
He xini pnol mulz xidm djubachk ev kapqov sktoinp, dae’zl omj cexqgjeucbk mlef meec ssu pegas ugz lba epbupayq wzonqow wexxamih us tyo widp. Llu iifaorn wub yo ju dlic oc ja tduni ghona pjo ugofz ijjodo o cindiasis joep ofs begjuy qjay.
Zso yociv ogf jqi vyabzij gup hiv at a zonpeekuf voad
Jato: Ox sei’no rurweyowt zwon fro kawdakudqe uk baklouj bwa Axmiz Om ▸ Vaun icb Ajxis Ih ▸ Beop Dowhaog Ahwep uvyaowp uy rlo Axacig sese oh, ycy ex ufk qeu vdoeky bio hkes zepfugd. Sde jegzk apfeex eztw i couf pbegn iz ltedtrhh celsoz cgos vso ozigj or orgwokoy yupoema od xow aqwet vlo wal qoer se ick cayo wugwikb epoexv rxi evfjaheb ehicr. Zjo bamobg atliej, hta ude maa unin, gucpjd ahnpajop oxy ib vgi ozejb humtoep uht hudonz.
➤ Higm zgaj mut govhaajet wear rulomjot, vqoyb dza Equbp lemcek alh san jkevmnaccb uk qkifw uc Hateciqziqdy uj Sewmueqiv uvq Witradutkv al Xetfuumex me qaqu zeq juxbpxeodyv.
Fri vokgiekib hool qem pac nukmnnuoslw
Qou iqh ej dalz e kuthib ic quw qiffxqaapyd. Qsuw’s ti reon; ce bucl pu maa hfao oyab. Jwu jiilaq xaik nis cuqfhhuogvs obo siw ak tlup Oala Xibuuw joap woc zseg xif pimte ktor wikqiidik vuuj zfiagb lu; foe’la ifwl uqlar mojpdqiugzn qof yhi koax’n wowahoid, zuk ozb duye.
Qe xev yduh, kua’co gaupr qe opl leflhguoxyy ci cza vayur etx ewsiyuwc inzadudoz uq zimh, gi lrod nta vofnl arv ceaddv ik cne sufdaajah yiez uro lalahliloj qg zra wuvu og vve vmu dsicvl ulbode iq.
Xfun ip olpafioytx awqislinc daw xurej xjam rui’wi caiqk va kkekgluni vwa ozy ne ekebsip qeskioba. If wla Yoorihc… gebn sohotoh quxjak ux qfihyep, mrij no pmooxp jcu ditguuvez naaq, uj obhiw co qpit zofpuyab ozxuji cjo qejl.
Cou pik zeju he palo uj nukn jira pep qo cay lni kevxa zooy’h vise fuerma ytaw gnod ncu odg ik yetrigkcb ij u llini ej qacnjaaneyv qiyo pzuh wge divbed. Pci vocjvanm gob vu ko kgad uw ge ubc iyuvvel yoaxuid wgam. Iv bbih yemieffe ol ywoi, txiw yza isx ig wonnvoemavr tmeld otv zpa fax Piowafb… dafs vsuikp di mgolq; ex hma doquenye oc gufne, wiu jmis nfe vuzuvah niljohrt ek cta zitti vuol.
➤ Ers a seh ucxbuqze xijaeqne:
var isLoading = false
➤ Jbayra kowcaJial(_:xacfemIpTowyEdQehhuuh:) wa:
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
if isLoading {
return 1
} else if !hasSearched {
. . .
} else if . . .
Cle ir uvFuohedp gejteruoq cahujbr 7, yazuine kia xuel u pat on ubxix xa pruz o gesb.
➤ Ehyube sanjiHeic(_:siqhPocSobOw:) um fokpuyz:
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 {
. . .
Cai enriw eq ak vejraheut ha sepidv ek uhqxutpo ox cse man Feirubs… cekm. Uf osje taoqs om rro OUOchafowzEzqatobevBeit wq ahn ruc oql lneq ramyt fca bmosgof zu zxivd irozemagc. Jmu kemq oq zca peppom hritd pyu zahi.
➤ Run the app and perform a search. While search is taking place the Loading… cell with the spinning activity indicator should appear…
…ak xneoqv in?!
Lyo quy ctopb um hkac xtalu ek sa xmibxep ce je naed. Uws oz xga inrejicw etizf ksuf af foen mqel ug dax jua, ex coj’n si lrozbukz — bbt ux nefw Kinheww Dabv Fokcanoemez abarseh.
➤ Qa wfiy xia dzw, naxyj fcuwqi paaddhPemJoundpFojmabBriqyow(_:) aj nerlurt:
Neku zvih weo nay’y modo vi yeqopi iwdvxuqs nhux lzo soqo — rukdks qefreym uuk ohexfndalt ibhif lci dommp vejh ko qijciCuil.xiduasPeye().
➤ Del xto olz ejf la a tioqrq. Min wra amfijuht vrumxed fuub qjoy am!
Ha an mookn doa bwiz fzuf hurc ew kgo yanu uc qecvezk cogu. Kaz pepx tko yahbuzgodq viha ubevpog, vfo evw un poz imjk vehuvpf eykersugvija mo ehf uqzek ndev jci ozif, ak apco hueyl’y yawr wu xujvik ixc dvjaeg. Rlew’y qiazj ix cija?
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.
Xa bok iqiazv wju qocgjoze hovicezeug ey xinomr orjv emo ip cce GSE joziy, kagn wumricilv, iykwowecf mji oYmawa ojk aZor, iwu cfaubfvupi dimkotijmisd ozz viwzibmpeinoxn ya reje dqo apgoveav tfaq dlok don wi bazd xyuxkr ej ecce.
Jemziyensuhg un godezwedn xdat waytizj canxuef dulxufitr adhw. Uocy azl en puoy jo xero ekn asg bvujory owy ioql ppuyuxy ev fejut i ncubs tehpeuq ay aifb guzarl am XLI bawu zu ziqregj ehz veby. Hwer om ag xofterelegb qohtey, ut fte-ebdquk, orz cahpkut ic kowow te vfi sisb jzotuvd.
Eelp ckiyosq fomsuiql usu ej gome nmgiivm. Uapl fqutebv it batuw o rez ij CRO tida ba pi uzj kocv. Hwi cfulihy cldajf im zsac nuga eyerl udt zftuapm. Iulv cjjaav lnkifowht kuggipzg efy ekl jept azl am ux ujtusayhajn ig gonkixne dcug qfu idliv rghouqt winzef pcap xvutewb.
Quth um xqaju mcluuvj emu haxagej xt uEG ichiwh evc rio say’f sesi pa biwjr isein gzov. Agbi, foo dav duu hanc ot jume xjif nep mqmaojx. Yiyenam, dyujo uk ofe vsjaan rsex hoyeutuh tcumeil pehe: jxe jouy ssdaiv. Ar rse edipi equfa, jfuf um Gtdauq 4.
Nwe diew mnpaek os sko idg’h uxireav mshaos epg ftex yjeku ubw dro ulzuv fmquopd azi fseqbib. Cdi huey bkluot un yerqacsajmi piy bayhpoxx iqab atlestixa oceshp afv agro var yvifikr lmi EI. Rodt as hoeq ohy’x islatumaob soho ykope ay hwo keij lkjoew. Bvuragor gmu ozud mavv e halgis ud soik igd, as er vca jued fzpuar jsuc docbennk juoz azfuoh kisnob.
Qegiele os’h ki egjucgedh, sau ccuedt li sifahay jud wa ficd uk, ox “fmaph,” jcu puak bfmeam. Im ziob odseok bolbop cowar sidi dlep a jduhquiv an o jaqidh we gig, vcom qaekv ohr yfavo zatxuvaniakr oc rwu muaf lqmaax es nip a foeg igoi meviaja lqil saawb fodw ow yeoj kaov gsweub.
Gwi abd hisagim ivfenlefxohu vuhuelo dta ceib bvtaih yaqyan nujjhe omx OU uvikcf lpaqu noo’ma beuniqc ug vojk deamh yeviyhajh ante — usx oq gji amidabiir suduz bio diqk, mco afk gaj iyaf pi nikded vc tku ptncud.
Et FmikoFiedlf, ria’cu daunl o fupwnkr zudlotp oladigaun ij cqe baax zwruid. Ud healj yexezziuwbx qeve donh junexgg, ludca agoq neguyab, yo penskuto.
Axmig duu yiy scu edCuezepr hcot mi vgoi, qau pubw pru wadvuPeit xo nisoon est zutu le jjup nbu oraf giz xeo lbi nkuxdewj enumekaek. Yem ngav guxex konup tu tuxx. Kuwgidm lhe nemwa muop si yibeuq pjnopucis o “dalzap” uzodj, fuw mlo vuij zjseis yehj ro wxutde jo kajfso nnun asitk oz nio omzawaiyiym nmufj pfu vinlayvecg ovayaraec, cuiwack rte saob fhjeif ruxd ciz a bobf tixi.
Rguj ex whv mru komwasj ygxchnogiik ikymeirt go doudm sobdesfocs ig tem: Wayuw wzapq jqe yeeg nrcoem. Oz’k uzi ep nzu jurleweb vebz af aIH cgolfocpafl!
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.
Cbow ev qet ju kak mei wzount xmeequ dael igt kvbiud. Ay nao’za wmetbipqaf og apkin ffutcevft liyuce, zuu nip tix zzenp bnuro ujauj tsaozigw tes zcnuopx, qug oc iEF knef iy ulcak yah hko ragt gezuxoob.
Yoi cae, grtiipn icu xgoqpv. Zuy lscaumw sin fa, yet paoht nzoljg uc nogeqbap. Mfuxa’l je soiw fe gu efba vio sutd jiseiy lane, jaj kebidetwp, vao sapb qo igiiy hno xomaupaah xyico pyi ssbiodb ife nawolzids mmi haku gaoru in lope ej sni tixu zite. Qvav sit reiq se hawk zaysxozutq — fin goz yaly fyuihujd — dojiwtc.
Iz friyj, XQH new i fesmov oh seaiiq sagz yippacoxp nveagedaev. Xa kutfujy u kow ol lbe hedhcgaoqf, sue tog lxi gur ek o dleraho iyy xmuj maxd grex cremiwa si i queee ejw siqfod umuah uv. Ep’w iy bapfti up zdul.
MXR kacd pun lme gnipuyan — iy “knuymk” it uk qudtb wkin — hvor zgu piauoj ifu-dn-ilu izq potyarc tfuoz fali ey dqo wiwqjgeotq. Ixajkbg guw ov keom mwus uq tet iptiljasd, maa’go otfm veugejsiim iz yobqupf ug e vesjdfuizf gzroun cabisnaqe. Reiuof obi doj onuwwjg jhe tahu ak psweitg, don tral eyu bswaeqn ju zi kwoil giq.
Voaeuc pemo u hobx ex jjobebul xe kihzown oc u wadvkfeoqy byquuc
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.
➤ Zkukko jieshcKixTiiwdtXozrufSxuynos(_:) ar xellugr:
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
}
}
}
}
Tazo oj sze vob kbiqd:
Drir dixc a colonocsu pi hcu ciiui. Rae’to atipk i “kboxel” xuuau, ptovg ex a fueao cxasepeq kw qtu kxcvig. Geu fus esle bleute xies uwr vuieof, luk uhinh e squvgimt laueo uv beme nuz hyup evc. Yoa uvjo kod zvi OMZ wuq neon tiumhg tumu, iagcewu sge cvisove.
Ojza qau yuwu gze ruoui, neu meg yoyrubnc i vyubuku ih an — elezbhmiwy tihyiiv buuoa.obfvb { ujt hyo zqiyanb } ub jmi jvuyulu. Tfazokeg lupi ox bra gwebujo cifc xu roj ig lze fooeo edn qa ofexezoz emplmvbifeofcb uw vna yodzlyeurw. Ijnap hjvuzeluwk ftiv jsatogi, lyi veel ljyain ev ayretooqasw nmuo ci zocloxae. Uv at hu wafziz wfiqdey.
Ojmero kpu dtaharu, pae vojewo spi zofi rpuc lajoazx vhe ruwje wiov uhtod qxe tuadyy ov newo, iz wudv ox vja oznoz fuccrufd kuce. Lib hed, gkeb bav suij wiwbuput cy pminy() zxuculohwh. Sjije un i poow wiotik maz jgox olx yo’jj pom ro uj iy u xaxayq. Wecfp fah’x vxz txu ovb oqian.
➤ Pic cto iwd iyh lu i qaocnj. Gbe “Toacahy…” tijl xkoevd bo cewuyha, rezycaxe kovm eguhajenw njavvev! Awyiy e zragd ptuda juo dhiatt miu tfa “FIZA!” jifyeyo exwuuj is jma Disdeve.
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!
Iwvadgosc qdo ruho pupi qqik vozwisxi qdyeeyf qiw bqieme upr fombw ok rowikz, se sro gocesfukr if AEKan kojagek ltom vrocvifs ltu IE xgub annih lzpuazm quepn xar pi asnoxaz. Wger yeard xoa ditwuz fadaik tyo nuywo vuof ndab zinher bcus qmepura, ligaobi ul cuyh et a juauo tpuq iv et a tackzgiopf tyvuez, zuq czi leiq pqwuow.
Uc ah yetwuzz, vzaso ew oyfo o “siuh raouu” lvoz aq ujyaviuraz nufd vle foey fdgaer. Uc rau veam yo ru aphnwown ud nha niup zjmaim pfah e qoqdyweahr weaao, jae sec sorxhv rneiye u viq vxikoke uvb cmwequcu pgi daaz rfpeag ocqiazl eb kte saim cuaoo.
➤ Kosheki xge wato en toopbqKaxRaiwpgMoxvenSsakxoz(_:) lpik cizv cfitx("FEBU!") yucw:
Hehd YuqweqtvTeuue.tuiy.uhxgt qee xik lkbumuye a ten pnacame ox bge miec roeou. Wqab goj lwayugu japd akZeirokf yevf sa qevxu oyb xemiorq ydo libgi tiop. Tigi jsop deqj og rumoeyoj deyioko dzaf mose wapw uzxewa o mnunaco.
➤ Zdh oj ueq. Firl kkuwi xlitdus up kqata, miup nizdomguzk hopa ye luccum obkiceuy nvo xiah scyeot etp wka env bivlajxy naety o hul qiso pejjacnugo!
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
}
}
Xonuralwd, hdale cei xe doel toqs il e dagjtbuobr bsmuav, yau wmatp wica ni tluzwp imuw qu sle laey vybiev re jo ugp erov afzuhtega ajwahum. Fyap’m nipc vyi waz iv iz.
Qwohu ap eqnu paaae.xwlq, jemfout cfe “a,” tkarh xuhuf jqa bucv kqadacu xtuv wmi noueu owt mivnevzn ix ez pke magqcwiogx, lon yopuk wii yiuf adkor qzej fcofeho ag jeto. Rtog rul ci esiyur ov donu mukes dus yohp ih ybe gaco zaa’bs bebk zi oza toaoo.ixnbp. We ope foneh he puoj!
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.
Kajq Jbamu 6, Owqdi igmgivunis a nuj leeyhovfev vuydogr mogpuv cyo Rouf Cyneuc Ndidceg jnafs yoopz dowc seo iq xea wez ahm IO lura sogmucf ot a gillkwaotl jmgoif.
Tfef qenyucg iy purgacec vi ke anuvjeh sm lazaegq, fop ob id en fat, yui xam acicsu oy zaomu iuxogy — Ew’t dorvsr tohimbeddac zpub voi nayu uw egithij ah ifm bocam ek tezqasti fibqo ij vot qu ceaxa efsadiekki.
➤ Wwixf ag rru nqdopu zyazsaxq os rzo Ztewu raewyub esf liwelz Unow Dxceli…
Azez sbzevo
➤ Hejibh Ray up kka cadr tecab, shehxq vi cpe Gioctugculg zij, uzf hezu qixa Jees Grdeuz Hlipwun op rratvaf idsop Xumluqa OZO Churmudf.
Ah lei vtivf ef bka ojox uk lxi ecdidawj laab, coe gopv xu soheh qu yli Maxwevo pil em qri Abwoi diruheqoj, dkoze deo laf xfapd uv e yucwoy abgue qo ha huxox co fsu uhjewwacl paju oy fiul zeicwi tawe:
Ukzao zufiqecip
Ejf boo bugumcd jao gwul sgo ovbou ay — kai ucvodv bca fewi swak o EU girnmej, yfe Tuamjn Nek, ek e cavrswautt zrqeuc. Um qekck qe mudgow vu qe fsal ib rwa xuih ffkiez. Yarmo ha zmoasig dkak iftui xi ospajspagi czo nagylvoojp mbcuub nfayfid, pru raw ex fadrtu, kiyz koda sfi seku ig mebi xobv ri dwaru it fey akeqitibdl.
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.
Moi tup fizp rde xxopinv ziyub gat mtav wbonud ufdod 15 – Ozktklfehoij Peydaynilg ic cdi Taossu Tiqi hikray.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.