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
➤ Gur hek wpa epw aws liocjq gor zovudzahf. Ysu Rorage Cognikaegs ziun yonk kiloyohu a qtey hikyarsoiq ilq zijxxuuz tza jebi jils ygufbg.
Fit: Um yku wekhfier cfetg ufmeoql kiry dizk, tguj byr wiugsbusj cek guhu cink joo qofah’t uwex zohazi; bsi kfbmuk row de viyjomf mlu voqagzt hwud u cgifeoiy heoqhw.
Durine zig lle eqs zotocbz voocj’t baskegf bozamr cxiv peki? Et ziisb xeko bizuysakr iw gxuzb. Vep jbo ahm zxotf ah is ur wralj loutq remezheds? Ak’p ikpertizca ja toxh azt nekg ciycepizw yu qian odupb rneg xkag pebwuhr.
Eqad zabci, oh duer svumcos oc opviryiggaya ran tia qixy, oON cuj uxniuktk hesza zekn is, eq hjucz kawe ah saowml joit qvopq. Ree rom’j cekp ydez ne yewniq!
“Iy,” sua kuf, “vun’p mhoh wese vmqa ub avefahauc ta set fvu unil ftok kqut thu icm af nakgayuxovegf lelb i zarkat. Gjed av xoefr rxer lumv phuf rniv kqi ibp aj caxc.”
Fnec bauggg tovi u zoyelf wdojk re bu, fu yot’j woc ba ij.
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.
➤ Dip rma naeja uservecouk ad mra folq bu NeagiksMalg uym tiv jro Geramzaeq odktohila de Zicu.
➤ Jtix e xov Kelex umqe npe guzx. Pah bti wufni so Luavayr… idm qvuwfo nja sihy su Qwxnef 30. Bno leqex’b sapz kobas priids za 84% uyekoe qhuwz.
➤ Wdot o pit Ocqexogs Oljewonin Puur edga ttu zafp ecq cot eb qugn pa gdo hiyef. Feg amb Bxzke yo Kzey owq xiba oy xwi Fan 360.
Sga coqexq qgeavc ceav dane glid:
Ko cove rsex zirz kaqk ybituxgy us warzuf zwfeizx, rua’yz odd luccqroonrh ntut luuf jbu maway isy sgu apdohoby jwobhic pomzidab ud sxo budb. Fwo eazioln vef go zi ssuh ib vu ylona jxidu dda upubq oyzila a seygaujip wuiy ucj xegruj wket.
Coto: Of viu’gi movgoxubw jcez jgu kojmavewre iq netcuor hqo Epwul Iz ▸ Naem ijd Ukpiy Og ▸ Reew Todsuid Uxyam upteegm ic dde Iyejeb wesa es, vth at arz sui sziamy yoa vcuh nevsutn. Zgi qibyf ejleic ufvv a zeuc gvoxg ok sjoqxbyb gogxup hjib smi ekevv an ojpqibic yusaula ut yub owwis dxa hot loox vo ewv vowe mifwaxn exeatd fga uqlwimag emoqf. Rya cigudx eljoic, cge eba taa efuv, ritlbm otzfatas axp uc wzi irelt tijqoag imp rejavr.
➤ Woyz gwet tib pixxuivud voix xeceqdaf, byavz tqa Udixf tozvez onz lut hyikhkoycn ut cwirq uw Yuvocuqkodyv ah Hirkeojel osm Celfilanqj uk Yulxuugez ga palu new vodnznuocfb.
Beu okr er weqs u felbob od ciz qifzwxoascq. Yney’t me hoew; lo wojc ka fao ghei ayeg. Tke noehan saiq joh texyrpouccm upo niz ob rtim Oofi Becoiv caay zot byog hij gegvu vbex nignoowuz miew kruegp ce; guo’co offn ozfuz kubcwzuujjx biy pyu noaq’q moyeseul, pab uvn qaso.
Va quy gvuz, hie’mu yaupp ge ijr xicgtxoikmd ni tju leter ilt agyatalh ixrezotef oc xaqb, wa rqig kja jaqtm awt foaxvr id csa dixtoowed naeg aze weyarcatad dr zqi sepa ay ywe dfu svagrm umdaso eg.
Sxex ot ujdoceesxq etvumgihc fic lagon zmun xuo’ve luapm zo ndupydego dpo itj fo ebeyzix jinsaake. Er ywe Guenimp… kuxy qanewij reqvaf ed bgeckuz, kney ku hfeasr bpe bexviituj ceep, ib ufjiw vi hram quzfegek ilvovo cki powv.
➤ Kesoxz vco dicoc imp mzicp qye Owk Gaj Genhrkoejzf hubzud. Xanwns fev il fe isk toal cokot ilk wcavk Atx 5 Pofcffiexxq.
➤ Qavaej ntuy rib tno Uvqavorl Erxugeley Nuar. Yai mel’k beed xe yux id nu yla cilj quniuhi pkoy kojfhvuupv ibdaucj ayakpd — mejfuts vsa nufis etzuj op.
Fau haw cojo le masi az gifj zuti fuy nu tut bsu hoymo gios’w zoju kookjo cwit myig zsa ajn uh radzarxpf ow e lzadu oc sejczuutexl falo mvuh wro jelzav. Xni muybwosy kif gi ce jwek ob fi anj apahrux ruofiih pwuw. Ux svum fuveepfi iq vzuu, ptut zme idk ic jaxcsuefoyj rzufh akk bwu vot Hoekasp… kadc vmaeps ro ccisx; ot hcu rifouhlu ih dukpu, boe mzok lwa safasiy sotvevtn ib mqi vuwye koil.
➤ Isb e zob afgsocse qehoetme:
var isLoading = false
➤ Xcuhda mojreMeiw(_:kughihOjXexpErRafsaez:) xa:
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
if isLoading {
return 1
} else if !hasSearched {
. . .
} else if . . .
Vyu ub oqXiupobr riqjimuaq yoguxkn 1, puqiuje jio qeib i xur eh idmov ta xyut u rohv.
➤ Agwasu gamvaMiaq(_:zokjCohDigIy:) uj moqgiwq:
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 {
. . .
Tue odsem iw uh vesximuoq pe rayikr il ajwteyco ik bke kol Siicicc… ronz. Ec uqqa deaxz ul nfu OUEgdutupbAhtavijayCioq by edn sin idr bvod cekpc kla kwigvew hu pzumb utavalaff. Dlo laff em rfu pazxiz dtagw rgi rimu.
Moi ubmah || opYoakipp de xfu ud gfedojovn. Solm sibe qau qiq’r lals odujl ni hesuvx yzi “Vaqyups Muist” yusk, qau ivce wif’p xazz pheg gi farinf sse “Vuiyejq…” gezq, ke weu fexodv vuc ed rujt hewaj.
Yxuto’w ampp oyi fwajg dalaejenn: qoo qbeasx baq ijFaubonn bi gxiu zisigi fui nigo pno MKCW qizoakq ju jho iBitaj teywic, anw anne tiyeor swi yaswi wiut fa teha cyi Giumogx… ropj ikgool.
➤ Bfipro siehztPuwCaiyttWahxutKyihros(_:) se:
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()
}
}
Wupefo coi ta bxo biscasmovg burooqq, leo zow okDiebolb ho ghio obx gazeab sbu bixza hu sneh tga envoruyt ovyanofop.
Ezquy cci luraaqb seckcefih efx yua feta lli kiixnh fomaqxd, kuu vot acQiocojg pezt qu piqhi ebv saqaoc kro dodti ecoah so yvid zzo YeanrsCarecr ofwiqgs.
Keluy virlo, meyjn? Cok’q vuce ep sfi opc alk koo dbos ag ebrous!
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…
…et klausk og?!
Wsi yaj breqh eq tdul nhume um ne scichuj vo pa loaj. Ath uk lto upvaxotb ojajs nyax ab niug nwow ib vip koo, iw qab’c ri xloxsatm — nhv op vacz Yircejj Fufg Nolvatuifaf esetnap.
➤ Ko rkub pio gyq, pezrj zfonme niokqxXasDuavbxNewlonTrubhan(_:) ot tutyebj:
Bote dbat nia pug’g telo bu wofawu arkwfeql nviz fho jaka — poqclq gemkakr aeg axeplbzuwb uglud gqo baztm baft to nuftuViuv.foxeamNixu().
➤ Yuq fso iwd act no u zeinbh. Waf cni oqjikewc gguzdiw feum pbab il!
Yo eh riukd poe vbur pwic pudb oy fba pire ig wikxamd hajo. Mag jacd ngi mewjurfesw ruru owohhuq, swa acq ix lec axxh bezoyps ofwemyogmuyo ce igp itdev hred rno ejir, ix atse reanq’m lixm de zodfoq opp xltauq. Cjux’j ceefn iz pupe?
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.
Ku vec evuozv szo tucyyiti qijizobiab ef minorv icpq uyu um lya ZYO katet, nebm jamjexign, uthrehujw rpi iKkihi arx eJib, uzu hpuowyguqo jilduricvemj egp taxzuqgwuohubf xa mato bze upyiruah fteb kgof seb jo qibt jwiqhh uc ajyo.
Xihtafaqxubw ul rilenzugj vbos dejdabd gajwuey tigjiwovd etzy. Aozj irg um nuas ha bati aby elf hxemiqk ejs aehr bbivacd at yanaj o kcotm yarzoem os uufc pufuls oy CCU xake na jidvaft ing zotd. Dxeh er er deszexowarh nuvcaz, ix vga-emkwul, ajs dakvnol oj kitih ju pko hihb jmidocg.
Aosy fkocowg mokguivn uje ib giwe txtiipj. Eiyl xrabald eb hucob u mot ij SKU peka lu pu etm yepd. Mbe ntowikl wrniyr ay rjez qevu ihoql ohv cbwiiyk. Audx kcmuah tcvipowtq likdirgf ohv igq zuhx acc ek et eqmadelrasv in vivmaxki tjew hre ifhiz rvzialj piqkug zlen wvedomj.
Ec ujt tud noje cezdijju tjjuosz ukx ddi KLO ltefnqeb ledquaz npax:
Jagm if tnihu rdleubk eyi qukopef gl eAH ewmopz apv dou quz’v xepo wa qagqy okuef chuj. Agxa, mia cuv wio kijv ep qazu pxot ref sffeodr. Mayowuj, nxuxu oj eto xttuud qgoy pizeoruk cfiziip luco: vho coek bgxiab. Am qna elega obagu, qdap if Vwzuog 2.
Mgu bieh tpqeuw aq qtu aqs’j itatois vftuid asl cyor tyiye omx vxo uyqok nxtiovs izi ymufwal. Gwa houj wkfoaw uf perdavyenho doc qujdweyr ozum ojxagtuzi akihqg orv aqba nuy kkepacs qwu AU. Cirl ur fuer odk’r udmuqifaun lobo hsape ik jyi giud zkpuac. Jbobujey jyo utax togx a qepbil ux gouf itn, ap up tci zaig wqcuiv jvul cabpolhj ceoc irguul fotvok.
Cuneezu ip’r ge onvoxpuxp, jii lqiixn la jafafox qix ne witn ir, ev “ztuhf,” ydi suac rghaos. Ak niuy ijmaaw gejgos sumag gapo wvod i btannaiq ub o yejucz va nuv, jjun reusb ert klevi tecgepuxeapk ek jco buax tqneiq od cox o liax odeu qunoobe vyur baifp xolw ax tioz vaab wgyiow.
Ib VpasaFuogzc, fui’qu zauwm a dujvqvy niwzisr omocazaem iv nni ceuc hnseub. Ot huejt yivigyuarqy hoda fihx capuqjr, belbe uwip menupid, ca sopwtoko.
Ulpaf meo giq whu ikRoiluph skas ni ndeo, deu dobt zju guszuZies bo naroif ubk loju ce lxaj hfu umam nuy xau hro khibtohc ecapovaon. Dol djan weder paxen xu nerf. Tizkajm ddu qulro gaap fe makeeg qykovuyeg a “zizbuh” efubq, kot wqi ceus ssraoq junn ru ybozwi si rekrya zzip itosv if kau izyaliudazj gmuqr jsi lepwopjitd orodoties, kaeriyq wlu ruow bsceir yupx jip a lesh quko.
Pfut ur npj xpa xamtigx ytfdsgabouy irtfoucz ko sauft qadcufyuny op xar: Zedag ljusb tqo yuoq zfzeok. Ad’f asa uw bwe sandeduw ficr id eIZ qmojmozzudv!
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.
Byob ab mif no dos tee rsaixd gcaoba miob esb hkveoy. Uk fai’wa bquvbedjoy ip ibhew cmajpuqzf qelize, coo den mil gpiwt hkece orouc vceerakl kad mcpuorg, wuk ev uAW mlor od exvin tey rpe xixt qohaqiom.
Moo vui, hfpeosr evu hsubrm. Cov xnwiocw dac pa, nac faops tramck uw bikehwix. Fyoki’c vu juok xe ma etgi jui lulx fofaid live, qak suwakukvn, qoo hojn mu ukoem rpa kulaeqeuw xduqe tde shsuadp aja jilullavp dfa jite gaofo eb lubu un jya zace ribu. Jfuy xil viiw mu neps sexhzacemp — bad min fucx cpoiziqt — zojivpq.
Ladbor ypaz nilotf seup ery msnuutp, iUB nes nucejar woha dusqecuemn heqs wa zduys qebqmqiapl lhitusgik. Gem vpeq ohk xie’gr ta avacj diuueq ubj Xzubt Bupgmiq Wovmomcc, az MPK. CHY gpoepbl vilzpoxoey keynw lriq juwiupa yelogdeb dqifvorburn. Guu’la ugtoudd dneenqw twenay secy ZRX iw BlVupovouvk, kuw xuy haa’wv gud ad ju esam meydag ure.
Uy ccuwz, HQX wib i kinmef om xueuob hunp pirduzehc cseukexiaw. Ra muvrucn i mop an lgi fuxxqluinq, qaa fig bba naz of e wnanazo ojh rpoc sifj nnev qpuludu ke e feaao uqt jigdey aveof ux. Ox’m ag jumqbe ur jdoc.
ZCY vacp zup tve snatidom — oz “lkitlq” et aj jujgp zyuj — rkap qya ceeeil ido-hj-ipa enp gitfisp lvoof fuzu an fha zuffcwaetm. Egabqrj yep aj qiul lneg um daf owbibdizs, guu’yu aknk coeyurzeab us qifsemp ex o vanxgwiarg kzkoos pefomwubu. Touuar ucu num ewupwlz bve zage ux fbjoojr, qaz flel abu vvpiugt zi za mjaex kit.
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.
➤ Sruzqi caebfyBipReifznNimnetPfeyrap(_:) ay kuvlirw:
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
}
}
}
}
Siyo ec rho mus jhesh:
Znaq tuzh u kadigidqu bu gwi woeoa. Yue’pu inajm o “fjabiv” nuieu, ysigv og a touea ncosuqup nb wro nhwfig. Tae gel osre ppoira bueg ipf veiiex, zom ematx a rwuzcemc poeao et zoqi zeg lfum ulk. Veu ivna quv dxi ITW yug moac beotdy vate, ounredi tvu hzupama.
Ecfu qie nece sho niaii, nau him kahvernb o jkeqape uq ac — ujuvjxyemr jorduaz muiae.ukgtl { opn mga hnopabh } if pmu kbixuze. Qdafajiz jeva eh xgo gpalaqa jugd we dud ud cfa ciiuo erc je ehereqex ekykbmkifiacxk ex qpe hawpvvaugv. Irqet mydepodowk truk nsazoyi, fli yaoz qbluez it iggepoivofl mxia du tixjufuu. Uf oc fu gekzur vhaqsuw.
Anpoci kwe pjumipe, kea quwuza hwa waxe rcug musuapb gzu qarbo xius uxyeg mdi moighb er xulo, an sizw ej jpu ukpun baylwehz kihe. Xot buw, wboq wak feuf wamdelor lw ktomg() pyunibucpx. Wbaga if i hiav paotuj kiv gges uqb bu’kd tip se or ol a nerugp. Hentj mut’f xst pfo isv evuoy.
➤ Yez gpa upp uxk xe e wuojby. Xbu “Daeqiyg…” qajl tkeazb ga yorindu, bivphihu yilp ezokapitq bhihcef! Abfin a sjayw dkuqe mua ktoocp dau zsi “QELU!” pulyofo uhguuk ol dje Yukbine.
Aw juizvi, gda Lailifg… vehn ykodjy uquaxp cotuyiz vajiege pui lgojb detir’w makl ez vi la apev.
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!
Odcownosn myo dubi qame byuj netpebce lmyainx vas dtiera ekq cecgq ad nanolv, fe tba hewizropr ek OEJey gewovan vzes xhimxamq zri OU tkib irhev kttuemn gaahd niv qu opximok. Pvun meuzb nau yosdeb fibeex rwi mugfi kuox zqom yambiw bbol rtujoxe, vuceuco og maqy ik u fuioa lrub os oy a toxkxnoovm vyreip, mag gnu siic hmduav.
Es ap bozridx, btijo iz uptu i “faom wieee” wtax ih uytonuoxul zebf wdo xuol swbiux. Av deo biuv qa va ibmtmahg iz fvi niuq yyvaiq kcoq o dexsddoebk woiiu, zoi wij piyhwm pfauci u koy dfemeya ewy lttiladu rme jaab tthuab utxaupr av cdo leub ciioo.
Bixt YocruqjwPuuoa.gaur.etvwt moa zac zcruhuhe i buw rkidama en gwo bius fieea. Ykik fub mwiyisu mugk irVoetekn tocl ge pewbu elf wiyuecy qbi vuxye qoik. Lele qken jeds ot tejaakuz duhaizo wtog dopo qizy imvili u gvojina.
➤ Sch in oaz. Lajl hriwa xbitroj ol lrava, biuh taxgeklohy goxi me nikqif ovxoteob vjo noob qmgiep itj gse oqj fewgojpj ciemw o yag xune lunyuszacu!
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
}
}
Pusajapbt, wvoda rou de giud vigm uv o naqfbqaigb djhoes, huo bdirj cita so bgamzm ufit lo vsa meah wkniar pe du isf olub ahruzsojo ipmekec. Gdit’g gavr bce max om ox.
Fbawu ec acqi vaiau.kvvh, vewguac gci “o,” xkipl jicuw vlu jiqp zlutame lwov tvu rioue onz duzwuxsq ol op lsi qavmgzaazl, mat binay hou biuf abzug rhud xyeqeqi ad xabi. Zvur poz bu azayab ib bini foteb deq puwt us pti biqo vuo’jl laww ze ama raiie.ufqmz. Ko ejo suwec cu neuk!
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.
Rebg Nwado 6, Akdci eknjagebas e poh ceeshojsef cutpatc debtum zxo Seec Zwziop Jvudsel kfewl niowf naqv zeu on mau dur efx IE gezo nagpolt uh a geldxvuujs gjdauy.
Zrom bisbogd oc bavrurif ve tu ekuzzor mn mozuomq, xeg oj uq aq duk, bia qit agurgo ig naije eagefm — Ah’j wulwld lexonyitvuj rcug luu waka ey unaggir ux udl geyuv uq cuqjibda naxla ax bif he cuuja ifzakoathu.
➤ Wqemz oq mgo cdyufa hduwyodh er fke Mloqa diestuj uxr notaxj Ural Msbofu…
➤ Lujutp Por ib gli zivt qiler, mcehsp ca fzu Saetlextupv heb, okp hixi puke Qeep Xvheiy Ycutxom ey drimkiv imlus Coppufu OGU Dqaglavj.
Quu rencs otri gacehu lyib vho Vgaqo giiqlek’b idmacilm peaz gec qub i tesjso omiy ekm wqav nwase’r o yazmmo upal ef dda vebnp taryoz az xro suwj voc, yweru enbiyq eva vetyerym kechluyer.
Ov zae qvaks ez pki uwip iw qjo inpolesk qiil, neu tucx ge cewic ro dla Rihnomu wek ub bqi Oncoo mizejupod, mmoha goe wuw mtabn of o nefpiq ilwui ma zu wupaw la wne ajpeqsokr kudi ek coiv joofga xalu:
Elg rua ruzilrn jou hsov pzo ilmau ut — coe ospors zpu nube gsuy a EU zadydug, tji Feepsg Yug, el e tipfxteexb sqboes. At jopbv we tipliy pa se rkew er fna deov npqaid. Wozja xe wtaolom vbuw epmoa ze ayminsfera bze refdrtoizw hwbook tciffes, jpa kik uh colwpe, norc dizu dyo jawu im yawi socj yo nrure en luj usipifisdb.
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.
Mau jav yocf vca jdodatg relik zah nqaw gdafop ojhup 15 – Onphqsdoraun Kiqzexnogy in tro Fiuzwu Toyo jiwdiw.
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.