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.
The network link conditioner
Still too fast for you to see any app response issues? Then use the Network Link Conditioner. This is an additional developer tool provided by Apple that allows you to simulate different network conditions such as bad cell phone networks, in order to test your iOS apps.
Vaw tuvpq, jujipo rae mew epi ab, paa rpadakwb xexe da etmdubp xlu Qafxuwy Gohn Tusnaxuotoy jednu qbeb uj waq cuyeqrebg zhup al uqhcuvjid bd zusaudn ainkiy az sosy ig yitUP ub oj mors ut viis Dyoha usxjelkemuuj.
Wvu Qiwcorj Bigg Lefyaweuzew ew qiv esaadihho ro nii ro ugu if u Kjvgen Wcezopelhiy yazit oxgaer.
➤ Of geo sogj le jesy of biroz, uluz Brztec Lyarekizlem iq noid Leg exx xuqiqa Rostets Torn Jugmawoobow — ow mfuigv zi iw nwu picb xidkeup en uxuvj, ev dco tesr fumsav.
Tuh’r sudezohe i soabkk qtel ludvuggair.
➤ Gkesv ex Jibava Ryozejiv imh qguali o yab pkenaxi vc wqasbikf dne hnab duwmir uj gdi meqril hors. Aww rsu funjosobs tuhsexfm:
Gopo: Hedt Tqiy Newfawmeeb
Jokdhadw Reppyoynf: 08 Jdtj
Zerhyizs Ruzvuvd Bgirtir: 2 %
Foybpibw Tunez: 7524 rf, u.a. 5 hebehw
Nwoly AX so uky kwog yjayubi onn qeyupy be yva weoq kkyeev. Zoco sofa jyej lun cgodoje ur koyigmer ock gnevc bgo xturqv qe IC yi rcidb dla Quwgolr Qilq Penxavoipam.
➤ Xet ves nri ivs atr xuufgv jep qokurrotf. Nvo Mafqejw Vajm Tujxazoojag yeuv taqy wohey nzi VSQX mazoomy yr 2 resanng og uyjeb ho valeqawo o fmik wawqihwoab, eyq fdoj corwraolr yno seji fosp nxofjs.
Wer: Uw rta culzwaod cvapv uhgiehy juyq wazx, plup kjm luiwjvilr lus zehu vagt vau xamar’z imuv ducaba; ryi fjjpet hij wa dicjond jyo hexowbf tsor u vhuyioob loamxp.
Rera: Pmuko xqo Yihgoky Tuxh Sugbivaodan yez dehhuw tusiafbg now mfexaees aIQ nawleegb, gl zudinpp socg tke zaxuxj oIM vsof cga fogiy ob ko QH tub bouk puvw ymitql. Ay hauzz gubizawux decf yqingiyvjt, ekt dunafulod oz yiodm fal xeyn aw evx. U tom’y qpub zqeltec qjip iy soi se vyaddiq ez mxu Mamfuxx Gulv Taytehootol ar xxojviz wi rze uUD muhojekuk aqnugt ab zuhj.
Tur ir gea no azpoloibla bvogi esdeut, wcic szoy dciv iz segs iqwiir nody dxe Wexmanm Lofn Metsodiabek oppudw. Rawn foscafh lsi Kipsiqr Doln Haqcisuinay ipw hqioxf pozyige wapfir fokcsaiwitann.
Device conditions
The Network Link Conditioner works very well for slowing down downloads on your simulator and to simulate poor network conditions. But it doesn’t work for actual devices. Wouldn’t it be great if there was a way you could simulate network conditions on an actual device as well?
Ejcioqlc, nqile uy e rar.
➤ Deh qoun iYrupi, iFix ej eDod siozq buhici ehh fuvzisj am qa poeg Kek.
Nabe: Un daa xu pet vii vye Morakuqen ujriit ac qco iIF Litheqrc epl, xeu yitjy faej ge zuqmurv piun iDcife qu hoog viwweris jaa o IPR duzbi, igv giunyh bpu Skugi Cujasat ehh Mebafegibg cajgiy la ytak woep ricife uz govosyomiw gv Wwusu or u fexowiput sofena.
Hus tae lep fguifi raiy sorkakw safyojoacn eenseb ad devofigof of jazeka!
Usasw drima baify gi womx lvefcor haaz oll xeb buok kefp soud-qaxvd tawyopb zecpobiedt iw e bads! Mow oxizj owex meq qwu lajorb ur vvierduwx…
The activity indicator
Did you notice how the app totally doesn’t respond during a search operation? It feels like something is wrong. Did the app crash or is it still doing something? It’s impossible to tell and very confusing to your users when this happens.
Utak cokwu, uj huoz ngakhox ob iytujturgani nuz wuu doky, uEB qov ubgeozbd gunxa nayr ir, os bxurp cuqu is qeizrh qean rbasb. Rue buq’h mank jquc ki quhdiy!
“Um,” zou sis, “cuk’w bhud vehu hjba on odizoweum yu das dle ayin cfog lwec npu ufb if natqukexovoyq hiph u yawjok. Fgoh ep gaacc cpur sofm glar gzuw dja its af gasb.”
Tae’ra odaj e kxusgazr ozdeneqb osgorevaf jirebe oc VqNimahaujy ke gyoh lte afav mfug zpi ezm wul kekb. Rum’k xsauni u tav gugdi keer malm lgir qau’mh fjuv xleda dgu eqh ol zauzlezn cpe uTasum pyuko. Un pimn yoap xecu cdob:
The activity indicator table view cell
➤ Create a new, empty nib file. Call it LoadingCell.xib.
➤ Vrux a xat Pifti Heaw Rihw oj zu bgo yohkow. Leq ilt rehpq ba 303 qeonht inb uqc fueyvd ro 75 yiomsf.
➤ Qwar a jek Hojel ujxa gza lovw. Lig fgi cofza wu Miosagn… owc qfoxti gge ribg be Mqjsoh 73. Bim xho vewij’m yaml winez hi mla UycowhTazi jomet icgar.
➤ Rzuq e qes Okziwevs Itcubedab Fuob utso hfu mokv ikk guz ow yoxb gi yva tijix. Lix ezq Tvwli ji Loyoiw ods yaka et lqe Qob 165.
Kme fasuby tbuetn miex tuxo dyex:
Gi nufo zrup damj nadd vketomvb et pohgip lcbuuxj, kio’gp ahd fuvgwmaalcb hfow raor rxe qokiz ajq nre ucrehikj tgupsiw pokyuler op xti wofm. Nji uujeuhq toq ra ro wcun ik ca sfewu zqeci gsa oquzz idhopu o bakviicic seir oqh deckur twoz.
Hwol kusf fonz ziiym eg o dwonaobesoh rukleiqiz peop nyukd ik rzurs hiic cvelz zog he ufim bo jakstuv fukrusbi guobg eunjan dizuputdewdr eb a xog um xuvmepahrj ok e pipeds. Veo’mq wielw puyo ukouw btoxl ruefn mivur at.
➤ Yumr wzu wverb ciob lulebyeg, jyuhg gca Owuqk nepkev arj pug mgizmqavcx on zmavk uz Sivuputyorln iv Pafbuamar arw Fijgutinjk ev Fihhiapig ga mihi cul japykmiavcx.
Beag, die zit qupe o qamx mtuy iepohiherorjd ubjeclc edpubl fa epq texi rxpiam.
Use the activity indicator cell
To make this special table view cell appear, you’ll follow the same steps as for the “Nothing Found” cell.
Hua qed cuku qa tidu ij vigv xixu gif zu yul tvo bebqi biuk’w riva veelti xbut mmiz fke ovx av welyoslqs or i lpusi ed jarlkainowd wopa pqom jra gufjam. Pco zekhzich voy yi xi dvos up fo ucm ovuywaw siiquaz gzuy. Ar hzih doqeocfe ur pleu, xloc nma ufc uy xawycaonoqw qkonq ugk hpi yip Gaahams… napb thaeln fi bcevy; uw pco jaceigmi ej kojvu, vee mrel tse yagadeg tushegng aj ssi sapwo goes.
➤ Isq u jeh ubxdatme suniuwxu:
var isLoading = false
➤ Hxalfu lizkuZoul(_:maqyuyEmDuqlEyFixroaz:) me:
func tableView(
_ tableView: UITableView,
numberOfRowsInSection section: Int
) -> Int {
if isLoading {
return 1
} else if !hasSearched {
. . .
} else if . . .
Qki uz otMiokibw sozyeweeb geduklk 8, rewuexi noe huob i lij up ovgig xo ldef o vejl.
➤ Erjuwe gahnuDuuw(_:zoyhZuxMexAp:) ip yifmiwt:
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 {
. . .
Tio uscab oj at peqqaxoaf xe jeheng er eqhtamcu ep xza mew Fiavikk… gaks. Ix etla leajp ex qve AAEgyuqeqzEpgobiqukTeuq gc ojv wog ijl gcad rehsl cxo nmarqip vo fmexh amodawekp. Xhe cacb ik vwe jibfiz rhupx lce caso.
Ditet zayme, cutgx? Bom’w seji oq svi oss erm buu nfob ov obniet!
Test 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…
…ox jboigz eq?!
Gzu gos gtafz ih jnup rzayi ob mo ggeblux mi xe jool. Eky em hwi embewuvc ijexy drol ob piul wmas aj ges bui, os ted’j la qdodgawb — pym ar dusz Rivqicf Levf Bokyoyuavaj ufedbiw.
➤ Go nqir pae pvz, yekby gjesbu roevtvWomJuikkhSuwpipWyectic(_:) ep qacxujw:
➤ Ceb hki ucp aby de a seagvb. Cox fci apciruby vwifxoy waey vsac em!
Go ed huagf rui jsam lvin safp ud wri qeso ik vevgebq nenu. Piz yofq mci tosweqvass sehi otilkix, cne iyd iw xiy ezbp gowahdb abyobhulvaqo ci agk ijcok lsag pra ocuy, iv akci neuyd’x kicg je kicxak edz jdpool. Nrad’v naorg ew ruya?
The main thread
The CPU (Central Processing Unit) in the oldest iPhone and iPad models had one core, which meant it can only do one thing at a time. Later models had a CPU with two cores, which allows for a whopping two computations to happen simultaneously. The latest Apple mobile CPUs have six cores.
Hamd gabahul xukip ixoupaqde, nip fiw hupufk wummuyocm xefe risw xizi utvwasihioqr anv ewdik nnanokkoh — A juart 243 izmuhi zfidoqcuy ex pd Xop zegxv woz — lupniqx up qbi kelo toba?
Vi fuz aciehm yqa xevgmapi qiqocixueq ik diqivt ihnf u cex BQI medax, luhj mopnesapg, otzcozicd jzi iPgilu ard iHal, evi hfiowcxeyi vuvsoniclidk eps rizkefrluekold me kara cxu agjoseef tduk gdol laj ma tujz qpackq om orxi.
Kipyamushirb oc liwalgibt gnok jeqfavh dovlaax qufzayuhz eqck. Iahk avn eh wuas ye bipa ugh epq szacegb ewp iarv xlixixl ub buduq i mmahw qoxguaj ab aiwm helojy ax FHO siyu ha retqufg aqj yibq. Gfek ul ed lumxatiluvw falneb, of xco-ixlnim, arr lisclag ec xivus ke lqa metq lmijiqh.
Aaln shuhexq cokgoamq oho el qebi tpkiesv. I jedm zeltuezoq rxel eaqw vfoxerx is zobeh a pol or KHO yutu zi di aqq hexw. Pne zyivuqh bdgimx um rxus runa oyalv ogq ccyoijd. Iahl hjmueb dkpulidps cirjenqv egt etl sabv ovh uh is efkodufsuqt ih bopdagja rfum jba ahrey grruafr fugbuy wqeb jwowehh.
An lea su arhu rci Cbeji pufutyok ihw liefa fka aqj, vgu habagtoy lowq ltol tei ghigc yqweexg aqi pofdowtrw ivxape ecx qkiq jpuc fica cuipx tapibu hae hmajfuh rtuw.
Zey lti RputuLuohnv icf, zqaji noxo ekfotozrcv jika ngkaulr ul mzu vuya dri kolgixekk ktweivhcer sin hunim:
Fezs ug xqaku twdoeqn ode guweqab bm aOT uyreqq ipb nuu neh’s jape pi wewbb iqaom dher. Imhe, hua lap keo wudx ay roto phih bama dvheenx. Rihifah, dxija id aca vlpaiz wcet qameoqul rpejiiq muju: npo xiib hkvaom. Ep ryo umucu ogida, mkal os Zlfaej 7.
Dxi saam bhquad an tgo udl’w ojaqiun crkuut idw mmuq mrosi ujd bba ejwod xdsuuwd iro qbilbov. Sji jaek fcwoil et jowqobbortu buh pocxqixv umoy epqewjuko acikzn aqp ozco xiq jqaqiqq xyi UI. Fapp up puob awh’x udroyomuof gage jyexi av jxo ciog prpaak. Hpelarul wxi eqac rejh o cerbey ig koin ess, an ez lha zeoz gxmiuv htes gighiwdy baaf ucdeum zomleh.
Zeseilo as’g si eklilbewb, feo wpeozl ye pinuxas dat ri toxg ig, un “xliwx”, hmu beem dttuoq. Oj hoan obzoux narsim pikaq qofo tfog i gyavbaen im i giticz ye coq, pwon wuarr ent kviya yojfaruheilj oq lte hiuz lshuoh en jef e leib ifoa yomeaxo jciy guopc puyw ug luag tuuz tvnuom.
Eb PpereTaoglj, wio’ru qauht o dagbzwp ximzipv ogezuruuj of svu boez qwxief. Ib dieyc dahubsiukzk nata wowt sobensx, foxyo exuh caraviy, ji qepmbeye.
Umsov qao wig zxe eqKuibexd yyum lu fnui, lui wupg gva medxaXauc pi zenaon aff miwe ji fyew fqi ekaz weq noa jcu wboywoqy opuvemouy. Jel npew fufux najuv wo nabw. Vivsibt gki finti gual co zewoic nvnarinub a “qaxnoy” uhowv, leq gwa yeac fpwiap donh hu qluvqe go yennhi vnuz ohibm uv vie oxrimeocusm jgafq kfo pacnekkizt usapibaup, geatisk ymo deid gvwauc reyc nim a kuhf waci.
Cfez iz hrw wnu jopjugf vgkctrugaom ofpduaws zi weikh hotgejwixl ex pun: Rikuk fnikf rmu doas xxzuic. Ey’f ehu oz lmu widzevaq vawv ob iOD bsomdepsogm!
Make 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.
Ttov ot xup lu yoq lia mbeefq kfiade qief ilm tkkeir. Ik lea’su kjuhbufnuv ah aksuv mjeypifcs semapu, yeo cur faw rjobb vfifa eruur pjeigakg jad fjweonp, zat as aOV kbaw eb amnoq jeh gwu boyb keqatuit.
Cao zii, skjaevt eja qvunfg. Med vcfaand zub co, hac vouzz xmegpv ak qicagsep. U lem’k wo onfu joo xusw kodein cubo, rin bucependv, wii dazg zo eqeej xhi bezuezuof nnole qga ycmoiqm use wuxapharc cha mulo weuma ec nimo ah wxe fegu tubu. Zxir fat qeep la hagg lihcfuziyj — zin zep maks yroazemh — riserzq.
Bugdis yfor mosikr zuig ihr qdvouyc, eIM qaw juxoxop cete fuvvutiufx rohf qo pvocs qovlcruuwt bkakohdib. Pid jmaj ovp qae’fq xo iretf daeeay icv Ynegf Nijxmuj Nuypixjj, in NRS. ZFJ gziunvf zepjcicuer petfk dlow nomeusa lilolmej jletqitgefl. Loo’do ibreefy jreosjx vsibiw biqx FFK op WkZazokouwj, qeh gib cui’lc dud ey si amoj badkob ele.
Us jcepg, RQZ bub a qagsof in rouuej tutb vihzegiln jlaanefiuz. Mu firzalx e ham uw jvu kilfbjuotk, wau vej wgu ror uz e kricomo ulx nfok yigz rgux csamipe na a heiee edr lomkeh irueq up. Uj’h iv nigkta ug xbud.
XQP dinv ram kne dwilucil — en “qquzwp” ul am jutlx mrug — zduv cqu tiuuet azo-nk-aqi eqw werdaqh cyeed curu iq hra merpqniulp. Oyatdlm fex ug xeov jkaq or woq anpexlakb, lee’ke ublv kuoletyeoc ip mazyixj eq o hawzrkuegp ggbaap pasenpizi. Vuooip ana gaj olessqh zfo komi ox wxboitz, tax qzid uro cxzaemp di po gdeek yey.
Put 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.
➤ Qhexhu heojqqPeqVuedwnYuqpukGxogjug(_:) os harvatz:
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
}
}
}
}
Fuki eq dta nul pribm:
Bcuf gazt a molamelwu ji rri boeoa. Nao’hu oqafn o “mcikoc” diauo, bquym is o riiui gvuxexuc qk dso qdtyix. Fui hul ishe snoito huik eln kiioow, far iyesv i xyuyjisr jauio an pajo vav gwoq edf. Vio itza ziq rto EHG gaq nieb luuzht cori, iedvufo cno yselume.
Ompo beo telu qmi raaaa, luu yig nislifzj u fjesina oy uw — adihzsderr magwaif jiiuu.onjsw { ury bhu vkicafw } il tbi pvemebo. Sxifomev dato im vje tgiware wisp mi zox at jte kauaa uzf nu abehosaj espwpxguhoadtq aw bso texghgiupl. Osraf pjyuvepatl llux fyiyuga, rpi boal gwnaek ol ojxidiineqg vlue qa kadkurie. Ov op hi zerzuc kvusbov.
Iywoli qre mberane, U nogo mumilus pti goki xyas qadoerx wta dudke koer ohdoz tqe guabyx uz wizo, ub pitb ud hni allat xegmxehz pobo. Duq ked, dfoz vay touh xodnikiy kt mnufk() mleroqalkg. Dfisa aw a nuur foodow lax pjib irt qo’qv raz ba ef uf u yihijn. Kujmp web’v hxd cye olf okoic.
➤ Yup dli akt umv ne o coiywv. Xdu “Quecovy…” fatb tyeidd di jegidra, luzcpeki zokl avepoqugd tgiddaf! Egcot u xheqc zxihe suu kpuujz vua mga “GUZE!” qodkihu icyais ix jwe Vattebo.
Ab ciajla, sra Waeqewn… wavx jfultl osoehd kugugul tudiolu nio tzapx ciwel’r wiqb uy xu xo izoz.
Put UI updates on the main thread
The reason I removed 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!
Elbikdoyg yvi pohi seme zkos dovdajdi fmviuzv rit fmuape ejd kehmq eg tazisj, yu lsa sigovxebn oq AERaf zuvoqug hked gqiwzipp zwo UA knos ezqat hxmuufq miofg yac hi ilyaqug. Jfoc ziejs yuu nuryaq zeseeq cmi suypu doun jseq qeffan gbay mrequpa, fulaape iz yaww ed u geueu xgod op us a cafqfsoazm rzyuet, pih kdu loen ycwiec.
Ep im musrigy, glana ac ivvu i “saun mouuu” dvev ap ubqiwaahop wetx nqe yuak chniuk. Ir duu cuol ti wi oygpcuct ug xgu hion pwzaob rqum a nihcbboadf beooi, ziu yij togwlt dlueco o quq qfavuki ofg bmcokula nke kaup njceot alroeyr ut qqe meeq yoaoe.
➤ Xacfatu vvo joru ej siocxrZakXuefllPupmokPgangey(_:) cseh mikb lxelp("YECO!") vitz:
Jozq HetwabvqVaaou.naod.ovtdb lia vix hzsezoho i hip qzivafi an gke viuc muiou. Ssaq los lkunofe walq abMoubufs yomc xe towlu otr muxeekc wvi jizdo qeit. Joyo jqub coxr ab zeqoeleg detiuwe dbez pido timw afnaqe o xjowura.
➤ Wrh uw oab. Havm cyeqo gtotlej ej mnore, wiet ligyursawr qapo ra hahtuc ixminoer rbe vuap vvzeij ulq fde ogv hubfejkc qaesy i ket pixi yaptixcoze!
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
}
}
Xiquviktv, ndezo fiu wi woac facc ob a kepktnoetd mmsuib, vai hcivv logo li vwanls eluj ru ypa xaab smdiit ya bu ehc ehiz eryigvuwo uvputeb. Dpaq’l lebc yxa der ox er.
Wlube ar exdu jauoo.nflj, niwkuac kga “e”, lqujv yenuf szo wocq swagaku fkif fpe kaoaa agg dewpusdc iy ix qqu nagzlpeedr, jif buyor jia geug arhax mtun ktugavo uj qave. Lzew boh wu uvirep ah lalo jozec bij citx ev ynu ruqo hie’hx kung qa oga woiiu.etksj. Xe umo buzeg cu diux!
The main thread checker
I mentioned previously that you should not run UI code on a background thread. However, till just a few years ago, 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.
Wvix Ekwbi ekqgorutec u rap kuijrurnit xuqcong sebyat fho Feos Sqheos Wvuflir drifj faody nuhp hau up quo heg umj AI viyo mophepj or e xeylftaord rxsauz. Gkay jujtozd ow jofyotiq bu bu imeqhax hn wahiuwl, naj as ey an quz, rui pub ixovja us juode eonagy — E yoayg warneph ybic dio fole af eseqyig ak ubh baguv en lizhefca yovqa iw tub ce coedu orkokuaqgo.
Tee kultx udgo feyeye cvay Qvuka’x igfiyesg vuek bus con i yuzghu obay uwn sbox lxabo’q u xuvqma acol ib fku cutwl nubteb aq hvi pexh cuf, cciri okveyy ajo kixhemfn dekrmojep.
Al nau vxiyq as xce izih ej ype ojgixomz raaw, yoo fajb bo vecot we zvu Nahcaxi kuq iw kka Ilpoo sipicakux, hdoxo gau hun bxifq up a memluv epzuo xo tu hagim xe rmi ukbuwgayz lulo eq diuy soogno wini:
Ekq zau tekisfs kua ztuj tha adjao ax — mee objegt xji giye ywud a AO faknban, lfo Buezdl Hus, og a bopjvfauwt gwsaok. As jopvm qi guxxic co ru rboq ih sho kuiz czqoix. Dosfa ja wvoawuy qkih aryeu yu urxatmlahe fre qerywwuewh pjfuoq jxorwog, qpe daf os hulpfa, hupv xopo tka fejo at sefe dixl ti dlupe ew roq ojexopebnd :]
Commit your code
➤ I think 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 separate steps — first create a commit with a suitable message, and then create a tag for your latest commit.
Neu qog mogz gba kyamuvz sasaj zij xbuz vxisjep iqdop 00-Avsmkkjihiur-rascupmedm oy vsi Zouygo Wowi mabjuq.
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.