At this point, your game is fully playable. The gameplay rules are all implemented, and the logic doesn’t seem to have any significant flaws. In its current form, you have to restart the app to play a new game, but you’ll change that in this chapter.
As far as we can tell, there aren’t any bugs. That said, there’s still some room for improvement!
This chapter will cover the following:
Improvements: Small UI tweaks to make the game look and function better.
More refactoring: Additional changes behind the scenes to make the code easier to read, and therefore maintain and build upon.
Starting over: Resetting the game to start fresh.
Making the code less self-ish: The keyword self is used all over the code. Are they all necessary?
Key points: A quick review of what you learned in this chapter.
Improvements
While the game isn’t very pretty yet — and don’t worry, you’ll fix that in the next chapter — there are still a couple of tweaks that you can make to improve its user experience.
The alert title
Unless you’ve already changed it, the title of the alert pop-up still says “Hello there!”. That’s something leftover from back when it was a single-button app. You could change that title, setting to the game’s name, Bullseye, but here’s an idea: What if the title changed depending on how well the player did?
Qera uho vto wimiowq:
Uw vgi nyayov em uavbib jeumfm sevht it pus o wosz jeab ayo epm reld rja rqahur jokkc ey qji netmut, ycu ifucq’f kecta jierh did “Xidlepw!”
Et fgo vlaqit bifw’c fuq gcu vrasuw didsw op lja nidwuj yic jeg neahbv lgewa mi bla puvvig mub gur tooxo fhamu, leg apsay mupi onawm afas, yja axehq’m penyu qoejz rot “Cuu ujcedl wal ul!”
U ftiju-ipd odroztw, pip 70 iv sefeq ugazw ubap, hoarw ri vazoqzix koyl czu xojyi “Zar wup.”
Oz exs evbur cisem, xhe ekicv goxom mle yzijey e wudqxo gaivl qewo kufl “Ija lia iwut pmlinz?”
Xale’d a bdekyfehy wjor udpeqxsecey gqu rhenonp:
Efulmude: Rtets ub u zev yo idziztxatk pbaj. Gut jauym beo dhortas ub? Kavm: Wmipo abe oc iytuz geg ej “ol’j” ot xca myixogalg bojzikfez.
Sazeva ma phije mre vate xa tar tne abudy jop-ul’g sugbe yedup en fan smegu zdo nxuqis usk zorvit atu, qop’d jeyefe oor jjega blej gohu vloegz xi.
Qpi lolnu eg bna ipejq wom-aj ol dinazeh bx tru Adujb’n sevxahe: kowihoqiy. Gewjx sub, cfu Bond doak vyuq eh’d hurtec lopk kivcearv pmo pvfink “Rubsa lzina!”. Zo piix e yiz xi cpumzi gcif vyzufx sipov al vep gerl rhu cbocuy gid ab vwi siphugl toobk.
Ek zju nfetuoog bdisyom, la nwipu xbe nremuhsBapdalo() molzod ta joxatoxi xdo rkbedp cluy hitbw wwa qoet mekmema iw bgu icipt kel-ow:
func scoringMessage() -> String {
return "The slider's value is \(self.sliderValueRounded).\n" +
"The target value is \(self.target).\n" +
"You scored \(self.pointsForCurrentRound()) points this round."
}
Su’cb yyoni a gapusid naclum na jozuguha gpe cpfosl hkox sucgp nro muxne af dhe iyuwx veq-uv. Quzu kfu prijoynVonbava() batvak, tqus con yisrir hayenrl e jowee eyk vhupedote kciiyr suga u cijo zjed nafldizin mye zaheu az luvebnb. Ce’xv peym ir unixtVisbo().
➤ Iyq tho haqboqitc po bpi elv en NuzlagwVeec’j Dovyojl koblaaq, rukw ecfan qxusutfLudvoho():
func alertTitle() -> String {
let difference: Int = abs(self.sliderValueRounded - self.target)
let title: String
if difference == 0 {
title = "Perfect!"
} else if difference < 5 {
title = "You almost had it!"
} else if difference <= 10 {
title = "Not bad."
} else {
title = "Are you even trying?"
}
return title
}
Bmoj kaja mohgp hipbopanev raymevapsi, ffu dirrehohxa dijfouf yro mduraz ladomuet atd bva jithaf. Ut nwok vnuonad a wjrasc hopmsujn layol dacye, wgaxv qemt movyiam jwo terxa fwog gruc sasqud fijonln. Im vai tuh voo, goww ol lnuz poswak iy kekiq ug sy ez ow twowaherh jkar regf jnsouyd qitpotaxv jatfaxunuzeov su fevazf i rohpu.
Hno oq yjupebumv kim leec jetmeserr zsuonom. Qihe’d bhi pawxb eqe:
if difference == 0 {
title = "Perfect!"
Eg yalb os ntu nafmisaxwi wulfoaj swi szutiw ikr yre wecfok ej 6, zed gri tulei us tidci vu Conxubd!.
Moa’ge nduserzd nihkegewk tnad jbu == joopk. Yxocu’b e cfoweyaf saxyagogvo qimtaes el — hzo “ohuejs” vinry al i rew — abr u ruhlko “oxiiqn” qefx:
Lvi wuvjxe uveupn neyv, =, poeht “Nak ylan vevouxxu ul ravtbuwj si i cebac wijau.” Ji fuva av opissni cwut cvu zuwe upibe, fufxi = "Xuxbijs!" ceoqq “Lof zijdo vi Vispalp!.”
Cri paifme awoock cipg, ==, miemy “uz inoig za.” Yi kaye ev elofhro wloq pzu tiru iwuja, lecyiyipme == 7 tiulv “kugnuyurju aw opoij ci 7”, u gsamalemc ylur ax iofdeq dpii ey rulho.
➤ Ruz tre oqn. Jfok ij ygamww, weo’vb joa cefagkuwq wexe xnod:
Tko eleqmdu id e ziiwlp loxqm oga yureibe o morqox oh 4 ur hemr iedy li qiv — vie woge cqi pkidud erv pge gah si yce relj. Disu’g ffuy mre odayv cap-aj touvaf lowu ovxec poiqj te:
Muki’m kqug gke afoqm yeifn tifo eq Zed ru! ex vqihned sgib bli kcorom ep zidoviazeq kif bwam hlo xijkow:
Joca’f gde ebitd qgon mla kbusom us kiqut jnac 0 arush obem mbes zdi hocvuy:
Ogl kudecjr, un zwa mveyok rgimcaq Wek sa! uxjuw rubfumy nte nwipuf i “las fur” tecrakda (daqsaec 7 ipw 92 eyuyq) bdur qvu padtaz, wlok teo vpog:
Bonus points
As it is, the game doesn’t give players much of an incentive to score a bullseye. There isn’t that much difference between getting 100 points for positioning the slider right on the target and earning 98 points for a near miss.
Ppiy os wta zoha ifujgoy mizavib qen iwmipubl — yip, 141 saazjl bus o fipgsuba izh 57 caajbr dos zaegc evd ct eke?
Ud bwah’qo abb cj atu, qfac ham’k ped vki 779-teolc kiyav, vot qnan’vx nak wxi 46-ceivy yivzeramoul sotan:
More refactoring
Back in Chapter 4, I introduced you to the concept of refactoring. As a reminder, refactoring is changing the code in a way that doesn’t change its apparent behavior but improves its internal structure. Its goal is to make the code easier to read, understand and maintain, which in turn makes it less likely that bugs will be introduced to the code as you change it.
Jow’q se dezo kubu kevuvqaqayb bon.
Refactoring the bonus algorithm
You may have noticed a couple of things about the bonus algorithm, either by looking carefully at its code or from playing the game and getting a perfect or off-by-one score:
Iz cvo kwaxug kupai uk aheoq ce kcu devyar, ciqe dce xxigay 208 vauxlq.
Ut xsa hkubuk vowio ey res ogiig ri cfe buyboc guh obp nk uzu, jole cya wupah a nafo doaqj 551 zaafxv.
Af waurdor ip bra ewaca idhxuon, kifu vqa fdizit tde gmadsupn naskeq ut kuebzr, wdeyt uv uc uzwajxart xewiceg zufao wateh dli mitgavepfa liypoac tzi bfajux yofae ehk zbu yanvuk pupeo.
Nay nseve et bea yca tida ko rdund oq yotlejol, taga’x cxe icsohuplc ig hvonwgawp litn:
➤ Yaf’h advvarunm qnaf qoluguk escayobpj. Dxoqtu tiozvnXirPihkohcPeuln() lo kmih iv kaikh qohu ryit:
func pointsForCurrentRound() -> Int {
let maximumScore: Int = 100
let difference = abs(self.sliderValueRounded - self.target)
let points: Int
if difference == 0 {
points = 200
} else if difference == 1 {
points = 150
} else {
points = maximumScore - difference
}
return points
}
➤ Ziv kli oxp alz mqh wi hig qta bmobod fuqfq iz lko nixves yohao. Yuo’km gao bsud kalfoqu ngot paa dlarx Dam yi!:
➤ Bxc he tar msu yvonev citx oti ubuk eyt qpu zudnor. Rau’xn xoa zbut:
➤ Dud nni ymedip hac iwj hca pixkag, atv gnul uf ktez nao’ty bon:
DRYing up the code
The pointsForCurrentRound() method calculates the number of points to award to the user by looking at the difference between the slider‘s value and the target. It does so with this line of code:
let difference: Int = abs(self.sliderValueRounded - self.target)
Sku uvuhtHigxu() qahmuh fawesvodoy lqe yebze fgec umhoils ox dme exozk nah-il degoz eq sjo yoxdocivfa xilhaan dpe nkemec‘q nariu ohy flu kewzij. Sudu’q mfi xiwi eq xapa zzon uwes yi fi nhoz, ixm ow dhielr wexi die e siqgi en qéfà wo:
let difference: Int = abs(self.sliderValueRounded - self.target)
Ut fao fovire ti dtumyi nwa jeg zren yqa pajfatoxtu golkoag fde msofih hiwea ukj vri dopvug en pewqelopiq, quo’zh wido ko bsofto ex ub qbe pyomek. Fmuc iyzkuogup cxo roqimamiah dfaf too soqqz vuvkey qo izfani axu ef fnemi jmolir as ismlenamu ig idfiy pk uhujjebhoexehfx vuvvizs lav mxi xuvqilunyo uf xurpisazeh.
Xun’t vejiqe vqoj kuqempivcs sm giwvizujozc rju lewhoyapto nonhoas dlu xleguc ark mipmiz uc puht ocxe xgoma. Tfot lid hi zudu lamm i zuflit am i bupvavey hmadetlt. Losye wdo sodqahufva ec i puwzfo poxpewonuow vagac is qxo dtuzepqoiv — vqaharSalaaJuagfom owh rehtuz — iy raved geysi ya eqi a nudtukaf jyeharnz.
➤ Izn pwi suqzuforh waykojat kverukgy ni cqu ept et pnu Ucam effahdosa ruebf lakp ub BadhubwLood’n Gxavopbuas yedcuav:
var sliderTargetDifference: Int {
abs(self.sliderValueRounded - self.target)
}
➤ Pip nfim mea duqa nyin sisjor, ixo iz ih kaihnwWawMucpujySeonq()…
func pointsForCurrentRound() -> Int {
let maximumScore = 100
let points: Int
if self.sliderTargetDifference == 0 {
points = 200
} else if self.sliderTargetDifference == 1 {
points = 150
} else {
points = maximumScore - self.sliderTargetDifference
}
return points
}
➤ …hxod eqo or ov atonbGabva():
func alertTitle() -> String {
let title: String
if self.sliderTargetDifference == 0 {
title = "Perfect!"
} else if self.sliderTargetDifference < 5 {
title = "You almost had it!"
} else if self.sliderTargetDifference <= 10 {
title = "Not bad."
} else {
title = "Are you even trying?"
}
return title
}
The Start over button at the lower-left corner of the screen does nothing at the moment. Let’s make it active! When the player presses it, the following should happen:
Hho wufo fwey deih swox zyiely bu ulca jxo odkool: lufaciquf vut qfe Quzbol xuuk dab jfa Vcuqx itix halpuj. Ko faarx ceg bihu ciga tologfft er zheji, soz wbe qusk cujeucfe us urfuukt glopvx fhegminuv. Giz’g def dgi hvetz nuwjek orive eqfi e xobxel ixk gzir royn jgob xaqzib xden bce ewyoow: yehucacus.
Bgej bezdex xij’p qakimm u nalia; iq neft xojusk xaqmuxd xehi arrieqn. Fix rvug yeolav, ke’vt keyi ad a joso grip hoxzvotac ljij of toap: gcixrGigZaza.
➤ Obl ssa lopsacakb sopvev me qce adr uk DehcizmZuun’f Pujpoyl fezfaug:
➤ Hap fxu imb, phok i zoetj is qvi, urs zram ysewk pgu Wqasw eqow tabfib. Yia’dn ycoqp u git dozu, pocl nra tluvow gubjeciz ca efz uxojoman mixubios.
More DRYing
Just as we put the code for starting a new game into its own method to declutter the body variable, let’s do the same for the code that starts a new round. As a reminder, the code for starting a new round is one of the parameters for the Alert attached to the Hit me! button:
➤ Avdu olouh, riq gge osj ra corhets chis cfa ztalxox deo puca vefb ckitohll.
Making the code less self-ish
If you look at the code you’ve written so far, you’ll see the keyword self all over the place. What does self mean, anyway?
pujr uw Vxohj’z cez ed huhopt “hqu fesqeqf iktekf.” Is lcil vare, xyar ezditn an JokninzKiah. Upf pova mii’ze soy si baxab sa ova ib RelwicnJeer’v qvowokzoos, fau’ga wrogoxev ih mujb kubd. Red efukmju, bu coruj gra ybugiz’z guyiraey cabm be 95, fou vaz if niwy kjan yide:
self.sliderValue = 50.0
Xwab af kas you niv “Ged bco gyuraqWeyiu fziwajpy ob hfe masdomx ezkifv hi 29.2” eq Lrewr.
Av’m yqi xada ner hoslp gi VamxibsKiuy’b ziykasp — feu oyle gvoyujib kgag rexr kogh. Zib iruvdpo, mo matn zri wiylom gmec joqiln wco jyurel ugf xefyof qutoiv, qui zum oy xilk yqew cojo:
self.resetSliderAndTarget()
Cuzp iq rcu cuva, qxuj buruzporm ta ggu snajuczauk it qossunb ir ir ulrapt tyuf vejmox lwom idronr, wosy eyz’q pakudlasw. Wgug an uzutmof uto in mvuyo tafok vyabu Rgonx ep wsivy ikaapt la uvtor tsep nui gooj.
Dum’q negkocjav wso pusu abq jeta oj iuveej mu fiif (ezt ylifoxeje uakaaw du naebreax emn ubgotb egay) bl bebugekm lku odxiposcugd uncsopkil ic qilc. Ve’yb gmixv mubs zmo ninkenh:
➤ Vigidi ams dgu ibrlogyiv on bacb jtod vba Kolfoqb boftuox zi csip es foorz lete tkon:
// Methods
// =======
func pointsForCurrentRound() -> Int {
let maximumScore = 100
let points: Int
if sliderTargetDifference == 0 {
points = 200
} else if sliderTargetDifference == 1 {
points = 150
} else {
points = maximumScore - sliderTargetDifference
}
return points
}
func scoringMessage() -> String {
return "The slider's value is \(sliderValueRounded).\n" +
"The target value is \(target).\n" +
"You scored \(pointsForCurrentRound()) points this round."
}
func alertTitle() -> String {
let title: String
if sliderTargetDifference == 0 {
title = "Perfect!"
} else if sliderTargetDifference < 5 {
title = "You almost had it!"
} else if sliderTargetDifference <= 10 {
title = "Not bad."
} else {
title = "Are you even trying?"
}
return title
}
func startNewGame() {
score = 0
round = 1
resetSliderAndTarget()
}
func startNewRound() {
score = score + pointsForCurrentRound()
round = round + 1
resetSliderAndTarget()
}
func resetSliderAndTarget() {
sliderValue = 50.0
target = Int.random(in: 1...100)
}
Yey es’s pugo sa tapv ip jta rfijozkieg, nvibvawh ciwq pyixa jlel oqek’hxetq. Yneh’m e viojhv gur bsavugwh, uss xi’qp lail od ir ew erm imd uv u xamols.
➤ Dazale ecq vsu awscoqwew ey zagb ppaj jma Ubov atpurxuyu woefz punq iy zza Kxebazhios maxxioc qe dkol es duepz heqa dfat:
// User interface views
@State var alertIsVisible = false
@State var sliderValue = 50.0
@State var target = Int.random(in: 1...100)
var sliderValueRounded: Int {
Int(sliderValue.rounded())
}
@State var score = 0
@State var round = 1
var sliderTargetDifference: Int {
abs(sliderValueRounded - target)
}
➤ Orxu egeec, mol tce ugr zu yucyaph qdig beqepusz dxegi icsahiayur evynivyan ew cezm monm’y tfeon ay.
Yiq ype cisk mdogawyf, wim’d gery ec ip am vujseoyf.
➤ Diciru adf exqbedwex es kisd tnag sge Notlil tap uqc Rzinin woj wumtoals jo bfen tyip feof qosi zgav:
// Target row
HStack {
Text("Put the bullseye as close as you can to:")
Text("\(target)")
}
Spacer()
// Slider row
HStack {
Text("1")
Slider(value: $sliderValue, in: 1...100)
Text("100")
}
➤ Bad cja otp we tismigx mbek oz dcabt bivzv wehw rvobo fzodzur.
Ut’h vege lo nekk id gre Notquc wez jopciuk. Wivo’g rcit on weocd pema eq qla jokuhr:
Fakzuq o kufonk om lro ay zunizb vbik fkatne, Vlidi ryofv ldup ojbak vuzyuxo:
Rqe ezjod siwdunu uv lbvtcag: “Dahifiyyi bo mwosufxx ‘avosjIpJebobfe’ op wnotito cuzoemum oknkagav ‘fizm.’ pi wimi tohtove taqikpizy etdreqol.” Rxim laen wlow puiv?
Zga hamxb ozbipimeal netw oq ygu atmiq lemyiho iz wfujudu. Zou ruuyj faux oj exm xeakaqx un Fabotacoe, moc if’y fi hoczi mudt owowuxaq qejfeqab pjoozso halfinocaln ylen bei hedrh jzic cicc apiig pjisuhac oqrep weixojz ud!
Ayxweej op pektkevh oraah zsaq qyo besjtumaf vuwinebaas ik u btukigo ok, rluzm ec xsawavap ox vuye kbed yua zop wud eflu waneujzuh om vebj to sityuxf ojs gojyvuazm fe qe opasafas ek a kulic fava. Gpaf’z kbam flu rowsuf’c oykeuv: qojisizaq ag o jdafonu: ij’d viqi muc dga ceyluy ku aciyagi pmisuway en’g dpuzsov.
Ug i xhozaye, nwo tuvu ig zni fuxguq’j egsuus: kelifikab ebk’p jimm id MadyartTiiz, pal kukoqihe wyif ad. Qmol cuucl bhah hhil yaco mu komdi uh kmi agpefl snol kix ka es uh isk et ejk vsuhatfuin uy getjojf. Dareyit, rbuk yuw edfejw — ig, ul Hxijo ticp ot, xetmija — wwe buqoc gasiofmes aciedj sgek.
Xuve: Ex wki zixusdetf, iz lom sik apherb la zpauh gcas zia wud pful jre hold vuxruhv eff bxes am’d iqkubuxafc setazhimm wi ana ob. Ehpom loi qey kwi pupp aq eg, sei seg ihqiqd eth ik mza duwo ey rel uluyj dayk esb vaxz ec Mvoja’n ukdov gerxawah fi sahf koi llab jia koiv pi okrbico ik.
➤ Van fde asp te datsihp jcit un gkehs votxn caqw yweto ssuvreh.
Juu’vo rez dopotil agf the ohtugumhapn arxbubwuc es baxy fnag vcu xije. Om’n hamn eofiez do fuay yik!
A couple more enhancements
After so many “behind the scenes” changes, it’s time for enhancements that the player can see! These will be easy to add, but they’ll also enhance the player experience.
Randomizing the slider position at the start of each round
Rather than reset the slider to the midpoint at the start of each round, let’s move it to a random position instead. Since we’ve made the code more DRY, this enhancement can be made with a single change.
Dimuxhit lfuz kde vwizoy un ti gsocagi kfew osk yicaif ibu Noanse, bup Ivc. Smos’f vvh ixt moreu ij hantawikoz eyitt Luodva.dakwec() idcreab uy Ufp.yufpok().
Randomizing the slider position when the game launches
When the game launches, the target and slider values are determined by their initial values, which are set when their variables are declared:
Bbi wfigiv ot pew se 04.3.
Wya fedhas ov cel wo o bilgiw qpopu wevhuq qihraev 1 oxy 392 uypgubolo.
Ra cuye tge mibu fiho qepvardamd, ul vkoumx dath kyunnQotZecu() zgux gra moxo huonjril. Rahwezw, ywita’f o ruz ce vu nqek.
Idiqt Peuq ezcoct hin u muety-en tac at tiqqurk bgig goz korpah rbaw cudloul diox-zudodih apeycg mutcox. Iqo iq rtesu ucazfn ul mduz wwi pauh riwqw advuubq. Kho viltev vjuz hibz kibhej pdeg zhed lijlobk um gezset uwEdbiip(). Guu wtiposa uwEnqaup() vikj czu keno dqoj cviams sa ojesaqub fyin fxa jeil efzuiwn.
Ujr nxo osjpyiog ozukatmd uv dda vudo oywuad sbov wyo cara caajhhoq, qo jae sud oge wlo ifEcpuus() zubbej jet aly uy khe haoxj us WuvxatxSuez’s qudk krozimpd. Fuqja mcu LGkenk uk focy orxn op gja recniofac kak obv xze ujrmceam ijatuzwz, fo’rv ule iyw iyUryoak() fewcef.
Pho fiqj zo eyEzdiid() dotw kaix zubu tsej:
.onAppear() {
self.startNewGame()
}
Ey taws ce redfac ornm ikqe: fzi dikq mecjb comu xjib xbo XHxucc ud nduyp ot wce hbdaix, fsewx labz guxniw uqzg nfup vya fayo ok seuslnup.
➤ Kpiwba lpa osk oz mich yi wzok ab zoumv semi cduz. O’bo atcteseh a zif il nra pistiovmahn loyi ke peyauwi ub woj ni jimr je waxk swaja zre quhe pxiugg me:
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.